[BACK]Return to Architect.c CVS log [TXT][DIR] Up to [contributed] / brogue-ce / src / brogue

Annotation of brogue-ce/src/brogue/Architect.c, Revision 1.1

1.1     ! rubenllo    1: /*
        !             2:  *  Architect.c
        !             3:  *  Brogue
        !             4:  *
        !             5:  *  Created by Brian Walker on 1/10/09.
        !             6:  *  Copyright 2012. All rights reserved.
        !             7:  *
        !             8:  *  This file is part of Brogue.
        !             9:  *
        !            10:  *  This program is free software: you can redistribute it and/or modify
        !            11:  *  it under the terms of the GNU Affero General Public License as
        !            12:  *  published by the Free Software Foundation, either version 3 of the
        !            13:  *  License, or (at your option) any later version.
        !            14:  *
        !            15:  *  This program is distributed in the hope that it will be useful,
        !            16:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            17:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            18:  *  GNU Affero General Public License for more details.
        !            19:  *
        !            20:  *  You should have received a copy of the GNU Affero General Public License
        !            21:  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
        !            22:  */
        !            23:
        !            24: #include "Rogue.h"
        !            25: #include "IncludeGlobals.h"
        !            26:
        !            27: short topBlobMinX, topBlobMinY, blobWidth, blobHeight;
        !            28:
        !            29: #ifdef BROGUE_ASSERTS // otherwise handled as a macro in rogue.h
        !            30: boolean cellHasTerrainFlag(short x, short y, unsigned long flagMask) {
        !            31:     assert(coordinatesAreInMap(x, y));
        !            32:     return ((flagMask) & terrainFlags((x), (y)) ? true : false);
        !            33: }
        !            34: #endif
        !            35:
        !            36: boolean checkLoopiness(short x, short y) {
        !            37:     boolean inString;
        !            38:     short newX, newY, dir, sdir;
        !            39:     short numStrings, maxStringLength, currentStringLength;
        !            40:
        !            41:     if (!(pmap[x][y].flags & IN_LOOP)) {
        !            42:         return false;
        !            43:     }
        !            44:
        !            45:     // find an unloopy neighbor to start on
        !            46:     for (sdir = 0; sdir < DIRECTION_COUNT; sdir++) {
        !            47:         newX = x + cDirs[sdir][0];
        !            48:         newY = y + cDirs[sdir][1];
        !            49:         if (!coordinatesAreInMap(newX, newY)
        !            50:             || !(pmap[newX][newY].flags & IN_LOOP)) {
        !            51:             break;
        !            52:         }
        !            53:     }
        !            54:     if (sdir == 8) { // no unloopy neighbors
        !            55:         return false; // leave cell loopy
        !            56:     }
        !            57:
        !            58:     // starting on this unloopy neighbor, work clockwise and count up (a) the number of strings
        !            59:     // of loopy neighbors, and (b) the length of the longest such string.
        !            60:     numStrings = maxStringLength = currentStringLength = 0;
        !            61:     inString = false;
        !            62:     for (dir = sdir; dir < sdir + 8; dir++) {
        !            63:         newX = x + cDirs[dir % 8][0];
        !            64:         newY = y + cDirs[dir % 8][1];
        !            65:         if (coordinatesAreInMap(newX, newY) && (pmap[newX][newY].flags & IN_LOOP)) {
        !            66:             currentStringLength++;
        !            67:             if (!inString) {
        !            68:                 if (numStrings > 0) {
        !            69:                     return false; // more than one string here; leave loopy
        !            70:                 }
        !            71:                 numStrings++;
        !            72:                 inString = true;
        !            73:             }
        !            74:         } else if (inString) {
        !            75:             if (currentStringLength > maxStringLength) {
        !            76:                 maxStringLength = currentStringLength;
        !            77:             }
        !            78:             currentStringLength = 0;
        !            79:             inString = false;
        !            80:         }
        !            81:     }
        !            82:     if (inString && currentStringLength > maxStringLength) {
        !            83:         maxStringLength = currentStringLength;
        !            84:     }
        !            85:     if (numStrings == 1 && maxStringLength <= 4) {
        !            86:         pmap[x][y].flags &= ~IN_LOOP;
        !            87:
        !            88:         for (dir = 0; dir < DIRECTION_COUNT; dir++) {
        !            89:             newX = x + cDirs[dir][0];
        !            90:             newY = y + cDirs[dir][1];
        !            91:             if (coordinatesAreInMap(newX, newY)) {
        !            92:                 checkLoopiness(newX, newY);
        !            93:             }
        !            94:         }
        !            95:         return true;
        !            96:     } else {
        !            97:         return false;
        !            98:     }
        !            99: }
        !           100:
        !           101: void auditLoop(short x, short y, char grid[DCOLS][DROWS]) {
        !           102:     short dir, newX, newY;
        !           103:     if (coordinatesAreInMap(x, y)
        !           104:         && !grid[x][y]
        !           105:         && !(pmap[x][y].flags & IN_LOOP)) {
        !           106:
        !           107:         grid[x][y] = true;
        !           108:         for (dir = 0; dir < DIRECTION_COUNT; dir++) {
        !           109:             newX = x + nbDirs[dir][0];
        !           110:             newY = y + nbDirs[dir][1];
        !           111:             if (coordinatesAreInMap(newX, newY)) {
        !           112:                 auditLoop(newX, newY, grid);
        !           113:             }
        !           114:         }
        !           115:     }
        !           116: }
        !           117:
        !           118: // Assumes it is called with respect to a passable (startX, startY), and that the same is not already included in results.
        !           119: // Returns 10000 if the area included an area machine.
        !           120: short floodFillCount(char results[DCOLS][DROWS], char passMap[DCOLS][DROWS], short startX, short startY) {
        !           121:     short dir, newX, newY, count;
        !           122:
        !           123:     count = (passMap[startX][startY] == 2 ? 5000 : 1);
        !           124:
        !           125:     if (pmap[startX][startY].flags & IS_IN_AREA_MACHINE) {
        !           126:         count = 10000;
        !           127:     }
        !           128:
        !           129:     results[startX][startY] = true;
        !           130:
        !           131:     for(dir=0; dir<4; dir++) {
        !           132:         newX = startX + nbDirs[dir][0];
        !           133:         newY = startY + nbDirs[dir][1];
        !           134:         if (coordinatesAreInMap(newX, newY)
        !           135:             && passMap[newX][newY]
        !           136:             && !results[newX][newY]) {
        !           137:
        !           138:             count += floodFillCount(results, passMap, newX, newY);
        !           139:         }
        !           140:     }
        !           141:     return min(count, 10000);
        !           142: }
        !           143:
        !           144: // Rotates around the cell, counting up the number of distinct strings of passable neighbors in a single revolution.
        !           145: //      Zero means there are no impassable tiles adjacent.
        !           146: //      One means it is adjacent to a wall.
        !           147: //      Two means it is in a hallway or something similar.
        !           148: //      Three means it is the center of a T-intersection or something similar.
        !           149: //      Four means it is in the intersection of two hallways.
        !           150: //      Five or more means there is a bug.
        !           151: short passableArcCount(short x, short y) {
        !           152:     short arcCount, dir, oldX, oldY, newX, newY;
        !           153:
        !           154:     brogueAssert(coordinatesAreInMap(x, y));
        !           155:
        !           156:     arcCount = 0;
        !           157:     for (dir = 0; dir < DIRECTION_COUNT; dir++) {
        !           158:         oldX = x + cDirs[(dir + 7) % 8][0];
        !           159:         oldY = y + cDirs[(dir + 7) % 8][1];
        !           160:         newX = x + cDirs[dir][0];
        !           161:         newY = y + cDirs[dir][1];
        !           162:         // Counts every transition from passable to impassable or vice-versa on the way around the cell:
        !           163:         if ((coordinatesAreInMap(newX, newY) && cellIsPassableOrDoor(newX, newY))
        !           164:             != (coordinatesAreInMap(oldX, oldY) && cellIsPassableOrDoor(oldX, oldY))) {
        !           165:             arcCount++;
        !           166:         }
        !           167:     }
        !           168:     return arcCount / 2; // Since we added one when we entered a wall and another when we left.
        !           169: }
        !           170:
        !           171: // locates all loops and chokepoints
        !           172: void analyzeMap(boolean calculateChokeMap) {
        !           173:     short i, j, i2, j2, dir, newX, newY, oldX, oldY, passableArcCount, cellCount;
        !           174:     char grid[DCOLS][DROWS], passMap[DCOLS][DROWS];
        !           175:     boolean designationSurvives;
        !           176:
        !           177:     // first find all of the loops
        !           178:     rogue.staleLoopMap = false;
        !           179:
        !           180:     for(i=0; i<DCOLS; i++) {
        !           181:         for(j=0; j<DROWS; j++) {
        !           182:             if (cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)
        !           183:                 && !cellHasTMFlag(i, j, TM_IS_SECRET)) {
        !           184:
        !           185:                 pmap[i][j].flags &= ~IN_LOOP;
        !           186:                 passMap[i][j] = false;
        !           187:             } else {
        !           188:                 pmap[i][j].flags |= IN_LOOP;
        !           189:                 passMap[i][j] = true;
        !           190:             }
        !           191:         }
        !           192:     }
        !           193:
        !           194:     for(i=0; i<DCOLS; i++) {
        !           195:         for(j=0; j<DROWS; j++) {
        !           196:             checkLoopiness(i, j);
        !           197:         }
        !           198:     }
        !           199:
        !           200:     // remove extraneous loop markings
        !           201:     zeroOutGrid(grid);
        !           202:     auditLoop(0, 0, grid);
        !           203:
        !           204:     for(i=0; i<DCOLS; i++) {
        !           205:         for(j=0; j<DROWS; j++) {
        !           206:             if (pmap[i][j].flags & IN_LOOP) {
        !           207:                 designationSurvives = false;
        !           208:                 for (dir = 0; dir < DIRECTION_COUNT; dir++) {
        !           209:                     newX = i + nbDirs[dir][0];
        !           210:                     newY = j + nbDirs[dir][1];
        !           211:                     if (coordinatesAreInMap(newX, newY)
        !           212:                         && !grid[newX][newY]
        !           213:                         && !(pmap[newX][newY].flags & IN_LOOP)) {
        !           214:                         designationSurvives = true;
        !           215:                         break;
        !           216:                     }
        !           217:                 }
        !           218:                 if (!designationSurvives) {
        !           219:                     grid[i][j] = true;
        !           220:                     pmap[i][j].flags &= ~IN_LOOP;
        !           221:                 }
        !           222:             }
        !           223:         }
        !           224:     }
        !           225:
        !           226:     // done finding loops; now flag chokepoints
        !           227:     for(i=1; i<DCOLS-1; i++) {
        !           228:         for(j=1; j<DROWS-1; j++) {
        !           229:             pmap[i][j].flags &= ~IS_CHOKEPOINT;
        !           230:             if (passMap[i][j] && !(pmap[i][j].flags & IN_LOOP)) {
        !           231:                 passableArcCount = 0;
        !           232:                 for (dir = 0; dir < DIRECTION_COUNT; dir++) {
        !           233:                     oldX = i + cDirs[(dir + 7) % 8][0];
        !           234:                     oldY = j + cDirs[(dir + 7) % 8][1];
        !           235:                     newX = i + cDirs[dir][0];
        !           236:                     newY = j + cDirs[dir][1];
        !           237:                     if ((coordinatesAreInMap(newX, newY) && passMap[newX][newY])
        !           238:                         != (coordinatesAreInMap(oldX, oldY) && passMap[oldX][oldY])) {
        !           239:                         if (++passableArcCount > 2) {
        !           240:                             if (!passMap[i-1][j] && !passMap[i+1][j] || !passMap[i][j-1] && !passMap[i][j+1]) {
        !           241:                                 pmap[i][j].flags |= IS_CHOKEPOINT;
        !           242:                             }
        !           243:                             break;
        !           244:                         }
        !           245:                     }
        !           246:                 }
        !           247:             }
        !           248:         }
        !           249:     }
        !           250:
        !           251:     if (calculateChokeMap) {
        !           252:
        !           253:         // Done finding chokepoints; now create a chokepoint map.
        !           254:
        !           255:         // The chokepoint map is a number for each passable tile. If the tile is a chokepoint,
        !           256:         // then the number indicates the number of tiles that would be rendered unreachable if the
        !           257:         // chokepoint were blocked. If the tile is not a chokepoint, then the number indicates
        !           258:         // the number of tiles that would be rendered unreachable if the nearest exit chokepoint
        !           259:         // were blocked.
        !           260:         // The cost of all of this is one depth-first flood-fill per open point that is adjacent to a chokepoint.
        !           261:
        !           262:         // Start by setting the chokepoint values really high, and roping off room machines.
        !           263:         for(i=0; i<DCOLS; i++) {
        !           264:             for(j=0; j<DROWS; j++) {
        !           265:                 chokeMap[i][j] = 30000;
        !           266:                 if (pmap[i][j].flags & IS_IN_ROOM_MACHINE) {
        !           267:                     passMap[i][j] = false;
        !           268:                 }
        !           269:             }
        !           270:         }
        !           271:
        !           272:         // Scan through and find a chokepoint next to an open point.
        !           273:
        !           274:         for(i=0; i<DCOLS; i++) {
        !           275:             for(j=0; j<DROWS; j++) {
        !           276:                 if (passMap[i][j] && (pmap[i][j].flags & IS_CHOKEPOINT)) {
        !           277:                     for (dir=0; dir<4; dir++) {
        !           278:                         newX = i + nbDirs[dir][0];
        !           279:                         newY = j + nbDirs[dir][1];
        !           280:                         if (coordinatesAreInMap(newX, newY)
        !           281:                             && passMap[newX][newY]
        !           282:                             && !(pmap[newX][newY].flags & IS_CHOKEPOINT)) {
        !           283:                             // OK, (newX, newY) is an open point and (i, j) is a chokepoint.
        !           284:                             // Pretend (i, j) is blocked by changing passMap, and run a flood-fill cell count starting on (newX, newY).
        !           285:                             // Keep track of the flooded region in grid[][].
        !           286:                             zeroOutGrid(grid);
        !           287:                             passMap[i][j] = false;
        !           288:                             cellCount = floodFillCount(grid, passMap, newX, newY);
        !           289:                             passMap[i][j] = true;
        !           290:
        !           291:                             // CellCount is the size of the region that would be obstructed if the chokepoint were blocked.
        !           292:                             // CellCounts less than 4 are not useful, so we skip those cases.
        !           293:
        !           294:                             if (cellCount >= 4) {
        !           295:                                 // Now, on the chokemap, all of those flooded cells should take the lesser of their current value or this resultant number.
        !           296:                                 for(i2=0; i2<DCOLS; i2++) {
        !           297:                                     for(j2=0; j2<DROWS; j2++) {
        !           298:                                         if (grid[i2][j2] && cellCount < chokeMap[i2][j2]) {
        !           299:                                             chokeMap[i2][j2] = cellCount;
        !           300:                                             pmap[i2][j2].flags &= ~IS_GATE_SITE;
        !           301:                                         }
        !           302:                                     }
        !           303:                                 }
        !           304:
        !           305:                                 // The chokepoint itself should also take the lesser of its current value or the flood count.
        !           306:                                 if (cellCount < chokeMap[i][j]) {
        !           307:                                     chokeMap[i][j] = cellCount;
        !           308:                                     pmap[i][j].flags |= IS_GATE_SITE;
        !           309:                                 }
        !           310:                             }
        !           311:                         }
        !           312:                     }
        !           313:                 }
        !           314:             }
        !           315:         }
        !           316:     }
        !           317: }
        !           318:
        !           319: // Add some loops to the otherwise simply connected network of rooms.
        !           320: void addLoops(short **grid, short minimumPathingDistance) {
        !           321:     short newX, newY, oppX, oppY;
        !           322:     short **pathMap, **costMap;
        !           323:     short i, d, x, y, sCoord[DCOLS*DROWS];
        !           324:     const short dirCoords[2][2] = {{1, 0}, {0, 1}};
        !           325:
        !           326:     fillSequentialList(sCoord, DCOLS*DROWS);
        !           327:     shuffleList(sCoord, DCOLS*DROWS);
        !           328:
        !           329:     if (D_INSPECT_LEVELGEN) {
        !           330:         colorOverDungeon(&darkGray);
        !           331:         hiliteGrid(grid, &white, 100);
        !           332:     }
        !           333:
        !           334:     pathMap = allocGrid();
        !           335:     costMap = allocGrid();
        !           336:     copyGrid(costMap, grid);
        !           337:     findReplaceGrid(costMap, 0, 0, PDS_OBSTRUCTION);
        !           338:     findReplaceGrid(costMap, 1, 30000, 1);
        !           339:
        !           340:     for (i = 0; i < DCOLS*DROWS; i++) {
        !           341:         x = sCoord[i]/DROWS;
        !           342:         y = sCoord[i] % DROWS;
        !           343:         if (!grid[x][y]) {
        !           344:             for (d=0; d <= 1; d++) { // Try a horizontal door, and then a vertical door.
        !           345:                 newX = x + dirCoords[d][0];
        !           346:                 oppX = x - dirCoords[d][0];
        !           347:                 newY = y + dirCoords[d][1];
        !           348:                 oppY = y - dirCoords[d][1];
        !           349:                 if (coordinatesAreInMap(newX, newY)
        !           350:                     && coordinatesAreInMap(oppX, oppY)
        !           351:                     && grid[newX][newY] > 0
        !           352:                     && grid[oppX][oppY] > 0) { // If the tile being inspected has floor on both sides,
        !           353:
        !           354:                     fillGrid(pathMap, 30000);
        !           355:                     pathMap[newX][newY] = 0;
        !           356:                     dijkstraScan(pathMap, costMap, false);
        !           357:                     if (pathMap[oppX][oppY] > minimumPathingDistance) { // and if the pathing distance between the two flanking floor tiles exceeds minimumPathingDistance,
        !           358:                         grid[x][y] = 2;             // then turn the tile into a doorway.
        !           359:                         costMap[x][y] = 1;          // (Cost map also needs updating.)
        !           360:                         if (D_INSPECT_LEVELGEN) {
        !           361:                             plotCharWithColor(G_CLOSED_DOOR, mapToWindowX(x), mapToWindowY(y), &black, &green);
        !           362:                         }
        !           363:                         break;
        !           364:                     }
        !           365:                 }
        !           366:             }
        !           367:         }
        !           368:     }
        !           369:     if (D_INSPECT_LEVELGEN) {
        !           370:         temporaryMessage("Added secondary connections:", true);
        !           371:     }
        !           372:     freeGrid(pathMap);
        !           373:     freeGrid(costMap);
        !           374: }
        !           375:
        !           376: // Assumes (startX, startY) is in the machine.
        !           377: // Returns true if everything went well, and false if we ran into a machine component
        !           378: // that was already there, as we don't want to build a machine around it.
        !           379: boolean addTileToMachineInteriorAndIterate(char interior[DCOLS][DROWS], short startX, short startY) {
        !           380:     short dir, newX, newY;
        !           381:     boolean goodSoFar = true;
        !           382:
        !           383:     interior[startX][startY] = true;
        !           384:
        !           385:     for (dir = 0; dir < 4 && goodSoFar; dir++) {
        !           386:         newX = startX + nbDirs[dir][0];
        !           387:         newY = startY + nbDirs[dir][1];
        !           388:         if (coordinatesAreInMap(newX, newY)) {
        !           389:             if ((pmap[newX][newY].flags & HAS_ITEM)
        !           390:                 || ((pmap[newX][newY].flags & IS_IN_MACHINE) && !(pmap[newX][newY].flags & IS_GATE_SITE))) {
        !           391:                 // Abort if there's an item in the room.
        !           392:                 // Items haven't been populated yet, so the only way this could happen is if another machine
        !           393:                 // previously placed an item here.
        !           394:                 // Also abort if we're touching another machine at any point other than a gate tile.
        !           395:                 return false;
        !           396:             }
        !           397:             if (!interior[newX][newY]
        !           398:                 && chokeMap[newX][newY] <= chokeMap[startX][startY] // don't have to worry about walls since they're all 30000
        !           399:                 && !(pmap[newX][newY].flags & IS_IN_MACHINE)) {
        !           400:                 //goodSoFar = goodSoFar && addTileToMachineInteriorAndIterate(interior, newX, newY);
        !           401:                 if (goodSoFar) {
        !           402:                     goodSoFar = addTileToMachineInteriorAndIterate(interior, newX, newY);
        !           403:                 }
        !           404:             }
        !           405:         }
        !           406:     }
        !           407:     return goodSoFar;
        !           408: }
        !           409:
        !           410: void copyMap(pcell from[DCOLS][DROWS], pcell to[DCOLS][DROWS]) {
        !           411:     short i, j;
        !           412:
        !           413:     for(i=0; i<DCOLS; i++) {
        !           414:         for(j=0; j<DROWS; j++) {
        !           415:             to[i][j] = from[i][j];
        !           416:         }
        !           417:     }
        !           418: }
        !           419:
        !           420: boolean itemIsADuplicate(item *theItem, item **spawnedItems, short itemCount) {
        !           421:     short i;
        !           422:     if (theItem->category & (STAFF | WAND | POTION | SCROLL | RING | WEAPON | ARMOR | CHARM)) {
        !           423:         for (i = 0; i < itemCount; i++) {
        !           424:             if (spawnedItems[i]->category == theItem->category
        !           425:                 && spawnedItems[i]->kind == theItem->kind) {
        !           426:
        !           427:                 return true;
        !           428:             }
        !           429:         }
        !           430:     }
        !           431:     return false;
        !           432: }
        !           433:
        !           434: boolean blueprintQualifies(short i, unsigned long requiredMachineFlags) {
        !           435:     if (blueprintCatalog[i].depthRange[0] > rogue.depthLevel
        !           436:         || blueprintCatalog[i].depthRange[1] < rogue.depthLevel
        !           437:                 // Must have the required flags:
        !           438:         || (~(blueprintCatalog[i].flags) & requiredMachineFlags)
        !           439:                 // May NOT have BP_ADOPT_ITEM unless that flag is required:
        !           440:         || (blueprintCatalog[i].flags & BP_ADOPT_ITEM & ~requiredMachineFlags)
        !           441:                 // May NOT have BP_VESTIBULE unless that flag is required:
        !           442:         || (blueprintCatalog[i].flags & BP_VESTIBULE & ~requiredMachineFlags)) {
        !           443:
        !           444:         return false;
        !           445:     }
        !           446:     return true;
        !           447: }
        !           448:
        !           449: void abortItemsAndMonsters(item *spawnedItems[MACHINES_BUFFER_LENGTH], creature *spawnedMonsters[MACHINES_BUFFER_LENGTH]) {
        !           450:     short i, j;
        !           451:
        !           452:     for (i=0; i<MACHINES_BUFFER_LENGTH && spawnedItems[i]; i++) {
        !           453:         removeItemFromChain(spawnedItems[i], floorItems);
        !           454:         removeItemFromChain(spawnedItems[i], packItems); // just in case; can't imagine why this would arise.
        !           455:         for (j=0; j<MACHINES_BUFFER_LENGTH && spawnedMonsters[j]; j++) {
        !           456:             // Remove the item from spawned monsters, so it doesn't get double-freed when the creature is killed below.
        !           457:             if (spawnedMonsters[j]->carriedItem == spawnedItems[i]) {
        !           458:                 spawnedMonsters[j]->carriedItem = NULL;
        !           459:                 break;
        !           460:             }
        !           461:         }
        !           462:         deleteItem(spawnedItems[i]);
        !           463:         spawnedItems[i] = NULL;
        !           464:     }
        !           465:     for (i=0; i<MACHINES_BUFFER_LENGTH && spawnedMonsters[i]; i++) {
        !           466:         killCreature(spawnedMonsters[i], true);
        !           467:         spawnedMonsters[i] = NULL;
        !           468:     }
        !           469: }
        !           470:
        !           471: boolean cellIsFeatureCandidate(short x, short y,
        !           472:                                short originX, short originY,
        !           473:                                short distanceBound[2],
        !           474:                                char interior[DCOLS][DROWS],
        !           475:                                char occupied[DCOLS][DROWS],
        !           476:                                char viewMap[DCOLS][DROWS],
        !           477:                                short **distanceMap,
        !           478:                                short machineNumber,
        !           479:                                unsigned long featureFlags,
        !           480:                                unsigned long bpFlags) {
        !           481:     short newX, newY, dir, distance;
        !           482:
        !           483:     // No building in the hallway if it's prohibited.
        !           484:     // This check comes before the origin check, so an area machine will fail altogether
        !           485:     // if its origin is in a hallway and the feature that must be built there does not permit as much.
        !           486:     if ((featureFlags & MF_NOT_IN_HALLWAY)
        !           487:         && passableArcCount(x, y) > 1) {
        !           488:         return false;
        !           489:     }
        !           490:
        !           491:     // No building along the perimeter of the level if it's prohibited.
        !           492:     if ((featureFlags & MF_NOT_ON_LEVEL_PERIMETER)
        !           493:         && (x == 0 || x == DCOLS - 1 || y == 0 || y == DROWS - 1)) {
        !           494:         return false;
        !           495:     }
        !           496:
        !           497:     // The origin is a candidate if the feature is flagged to be built at the origin.
        !           498:     // If it's a room, the origin (i.e. doorway) is otherwise NOT a candidate.
        !           499:     if (featureFlags & MF_BUILD_AT_ORIGIN) {
        !           500:         return ((x == originX && y == originY) ? true : false);
        !           501:     } else if ((bpFlags & BP_ROOM) && x == originX && y == originY) {
        !           502:         return false;
        !           503:     }
        !           504:
        !           505:     // No building in another feature's personal space!
        !           506:     if (occupied[x][y]) {
        !           507:         return false;
        !           508:     }
        !           509:
        !           510:     // Must be in the viewmap if the appropriate flag is set.
        !           511:     if ((featureFlags & (MF_IN_VIEW_OF_ORIGIN | MF_IN_PASSABLE_VIEW_OF_ORIGIN))
        !           512:         && !viewMap[x][y]) {
        !           513:         return false;
        !           514:     }
        !           515:
        !           516:     // Do a distance check if the feature requests it.
        !           517:     if (cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY)) { // Distance is calculated for walls too.
        !           518:         distance = 10000;
        !           519:         for (dir = 0; dir < 4; dir++) {
        !           520:             newX = x + nbDirs[dir][0];
        !           521:             newY = y + nbDirs[dir][1];
        !           522:             if (coordinatesAreInMap(newX, newY)
        !           523:                 && !cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)
        !           524:                 && distance > distanceMap[newX][newY] + 1) {
        !           525:
        !           526:                 distance = distanceMap[newX][newY] + 1;
        !           527:             }
        !           528:         }
        !           529:     } else {
        !           530:         distance = distanceMap[x][y];
        !           531:     }
        !           532:
        !           533:     if (distance > distanceBound[1]     // distance exceeds max
        !           534:         || distance < distanceBound[0]) {   // distance falls short of min
        !           535:         return false;
        !           536:     }
        !           537:     if (featureFlags & MF_BUILD_IN_WALLS) {             // If we're supposed to build in a wall...
        !           538:         if (!interior[x][y]
        !           539:             && (pmap[x][y].machineNumber == 0 || pmap[x][y].machineNumber == machineNumber)
        !           540:             && cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY)) { // ...and this location is a wall that's not already machined...
        !           541:             for (dir=0; dir<4; dir++) {
        !           542:                 newX = x + nbDirs[dir][0];
        !           543:                 newY = y + nbDirs[dir][1];
        !           544:                 if (coordinatesAreInMap(newX, newY)     // ...and it's next to an interior spot or permitted elsewhere and next to passable spot...
        !           545:                     && ((interior[newX][newY] && !(newX==originX && newY==originY))
        !           546:                         || ((featureFlags & MF_BUILD_ANYWHERE_ON_LEVEL)
        !           547:                             && !cellHasTerrainFlag(newX, newY, T_PATHING_BLOCKER)
        !           548:                             && pmap[newX][newY].machineNumber == 0))) {
        !           549:                     return true;                        // ...then we're golden!
        !           550:                 }
        !           551:             }
        !           552:         }
        !           553:         return false;                                   // Otherwise, no can do.
        !           554:     } else if (cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY)) { // Can't build in a wall unless instructed to do so.
        !           555:         return false;
        !           556:     } else if (featureFlags & MF_BUILD_ANYWHERE_ON_LEVEL) {
        !           557:         if ((featureFlags & MF_GENERATE_ITEM)
        !           558:             && (cellHasTerrainFlag(x, y, T_OBSTRUCTS_ITEMS | T_PATHING_BLOCKER) || (pmap[x][y].flags & (IS_CHOKEPOINT | IN_LOOP | IS_IN_MACHINE)))) {
        !           559:             return false;
        !           560:         } else {
        !           561:             return !(pmap[x][y].flags & IS_IN_MACHINE);
        !           562:         }
        !           563:     } else if (interior[x][y]) {
        !           564:         return true;
        !           565:     }
        !           566:     return false;
        !           567: }
        !           568:
        !           569:
        !           570: void addLocationToKey(item *theItem, short x, short y, boolean disposableHere) {
        !           571:     short i;
        !           572:
        !           573:     for (i=0; i < KEY_ID_MAXIMUM && (theItem->keyLoc[i].x || theItem->keyLoc[i].machine); i++);
        !           574:     theItem->keyLoc[i].x = x;
        !           575:     theItem->keyLoc[i].y = y;
        !           576:     theItem->keyLoc[i].disposableHere = disposableHere;
        !           577: }
        !           578:
        !           579: void addMachineNumberToKey(item *theItem, short machineNumber, boolean disposableHere) {
        !           580:     short i;
        !           581:
        !           582:     for (i=0; i < KEY_ID_MAXIMUM && (theItem->keyLoc[i].x || theItem->keyLoc[i].machine); i++);
        !           583:     theItem->keyLoc[i].machine = machineNumber;
        !           584:     theItem->keyLoc[i].disposableHere = disposableHere;
        !           585: }
        !           586:
        !           587: void expandMachineInterior(char interior[DCOLS][DROWS], short minimumInteriorNeighbors) {
        !           588:     boolean madeChange;
        !           589:     short nbcount, newX, newY, i, j, layer;
        !           590:     enum directions dir;
        !           591:
        !           592:     do {
        !           593:         madeChange = false;
        !           594:         for(i=1; i<DCOLS-1; i++) {
        !           595:             for(j=1; j < DROWS-1; j++) {
        !           596:                 if (cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)
        !           597:                     && pmap[i][j].machineNumber == 0) {
        !           598:
        !           599:                     // Count up the number of interior open neighbors out of eight:
        !           600:                     for (nbcount = dir = 0; dir < DIRECTION_COUNT; dir++) {
        !           601:                         newX = i + nbDirs[dir][0];
        !           602:                         newY = j + nbDirs[dir][1];
        !           603:                         if (interior[newX][newY]
        !           604:                             && !cellHasTerrainFlag(newX, newY, T_PATHING_BLOCKER)) {
        !           605:                             nbcount++;
        !           606:                         }
        !           607:                     }
        !           608:                     if (nbcount >= minimumInteriorNeighbors) {
        !           609:                         // Make sure zero exterior open/machine neighbors out of eight:
        !           610:                         for (nbcount = dir = 0; dir < DIRECTION_COUNT; dir++) {
        !           611:                             newX = i + nbDirs[dir][0];
        !           612:                             newY = j + nbDirs[dir][1];
        !           613:                             if (!interior[newX][newY]
        !           614:                                 && (!cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY) || pmap[newX][newY].machineNumber != 0)) {
        !           615:                                 nbcount++;
        !           616:                                 break;
        !           617:                             }
        !           618:                         }
        !           619:                         if (!nbcount) {
        !           620:                             // Eliminate this obstruction; welcome its location into the machine.
        !           621:                             madeChange = true;
        !           622:                             interior[i][j] = true;
        !           623:                             for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
        !           624:                                 if (tileCatalog[pmap[i][j].layers[layer]].flags & T_PATHING_BLOCKER) {
        !           625:                                     pmap[i][j].layers[layer] = (layer == DUNGEON ? FLOOR : NOTHING);
        !           626:                                 }
        !           627:                             }
        !           628:                             for (dir = 0; dir < DIRECTION_COUNT; dir++) {
        !           629:                                 newX = i + nbDirs[dir][0];
        !           630:                                 newY = j + nbDirs[dir][1];
        !           631:                                 if (pmap[newX][newY].layers[DUNGEON] == GRANITE) {
        !           632:                                     pmap[newX][newY].layers[DUNGEON] = WALL;
        !           633:                                 }
        !           634:                             }
        !           635:                         }
        !           636:                     }
        !           637:                 } else if (pmap[i][j].layers[DUNGEON] == DOOR
        !           638:                            || pmap[i][j].layers[DUNGEON] == SECRET_DOOR) {
        !           639:                     pmap[i][j].layers[DUNGEON] = FLOOR;
        !           640:                 }
        !           641:             }
        !           642:         }
        !           643:     } while (madeChange);
        !           644: }
        !           645:
        !           646: boolean fillInteriorForVestibuleMachine(char interior[DCOLS][DROWS], short bp, short originX, short originY) {
        !           647:     short **distanceMap, **costMap, qualifyingTileCount, totalFreq, sRows[DROWS], sCols[DCOLS], i, j, k;
        !           648:     boolean success = true;
        !           649:
        !           650:     zeroOutGrid(interior);
        !           651:
        !           652:     distanceMap = allocGrid();
        !           653:     fillGrid(distanceMap, 30000);
        !           654:     distanceMap[originX][originY] = 0;
        !           655:
        !           656:     costMap = allocGrid();
        !           657:     populateGenericCostMap(costMap);
        !           658:     for(i=0; i<DCOLS; i++) {
        !           659:         for(j=0; j<DROWS; j++) {
        !           660:             if (costMap[i][j] == 1 && (pmap[i][j].flags & IS_IN_MACHINE)) { //pmap[i][j].machineNumber) {
        !           661:                 costMap[i][j] = PDS_FORBIDDEN;
        !           662:             }
        !           663:         }
        !           664:     }
        !           665:     costMap[originX][originY] = 1;
        !           666:     dijkstraScan(distanceMap, costMap, false);
        !           667:     freeGrid(costMap);
        !           668:
        !           669:     qualifyingTileCount = 0; // Keeps track of how many interior cells we've added.
        !           670:     totalFreq = rand_range(blueprintCatalog[bp].roomSize[0], blueprintCatalog[bp].roomSize[1]); // Keeps track of the goal size.
        !           671:
        !           672:     fillSequentialList(sCols, DCOLS);
        !           673:     shuffleList(sCols, DCOLS);
        !           674:     fillSequentialList(sRows, DROWS);
        !           675:     shuffleList(sRows, DROWS);
        !           676:
        !           677:     for (k=0; k<1000 && qualifyingTileCount < totalFreq; k++) {
        !           678:         for(i=0; i<DCOLS && qualifyingTileCount < totalFreq; i++) {
        !           679:             for(j=0; j<DROWS && qualifyingTileCount < totalFreq; j++) {
        !           680:                 if (distanceMap[sCols[i]][sRows[j]] == k) {
        !           681:                     interior[sCols[i]][sRows[j]] = true;
        !           682:                     qualifyingTileCount++;
        !           683:
        !           684:                     if (pmap[sCols[i]][sRows[j]].flags & HAS_ITEM) {
        !           685:                         // Abort if we've engulfed another machine's item.
        !           686:                         success = false;
        !           687:                         qualifyingTileCount = totalFreq; // This is a hack to drop out of these three for-loops.
        !           688:                     }
        !           689:                 }
        !           690:             }
        !           691:         }
        !           692:     }
        !           693:
        !           694:     // Now make sure the interior map satisfies the machine's qualifications.
        !           695:     if ((blueprintCatalog[bp].flags & BP_TREAT_AS_BLOCKING)
        !           696:         && levelIsDisconnectedWithBlockingMap(interior, false)) {
        !           697:         success = false;
        !           698:     } else if ((blueprintCatalog[bp].flags & BP_REQUIRE_BLOCKING)
        !           699:                && levelIsDisconnectedWithBlockingMap(interior, true) < 100) {
        !           700:         success = false;
        !           701:     }
        !           702:     freeGrid(distanceMap);
        !           703:     return success;
        !           704: }
        !           705:
        !           706: void redesignInterior(char interior[DCOLS][DROWS], short originX, short originY, short theProfileIndex) {
        !           707:     short i, j, n, newX, newY;
        !           708:     enum directions dir;
        !           709:     short orphanList[20][2];
        !           710:     short orphanCount = 0;
        !           711:     short **grid, **pathingGrid, **costGrid;
        !           712:     grid = allocGrid();
        !           713:
        !           714:     for (i=0; i<DCOLS; i++) {
        !           715:         for (j=0; j<DROWS; j++) {
        !           716:             if (interior[i][j]) {
        !           717:                 if (i == originX && j == originY) {
        !           718:                     grid[i][j] = 1; // All rooms must grow from this space.
        !           719:                 } else {
        !           720:                     grid[i][j] = 0; // Other interior squares are fair game for placing rooms.
        !           721:                 }
        !           722:             } else if (cellIsPassableOrDoor(i, j)) {
        !           723:                 grid[i][j] = 1; // Treat existing level as already built (though shielded by a film of -1s).
        !           724:                 for (dir = 0; dir < 4; dir++) {
        !           725:                     newX = i + nbDirs[dir][0];
        !           726:                     newY = j + nbDirs[dir][1];
        !           727:                     if (coordinatesAreInMap(newX, newY)
        !           728:                         && interior[newX][newY]
        !           729:                         && (newX != originX || newY != originY)) {
        !           730:
        !           731:                         orphanList[orphanCount][0] = newX;
        !           732:                         orphanList[orphanCount][1] = newY;
        !           733:                         orphanCount++;
        !           734:                         grid[i][j] = -1; // Treat the orphaned door as off limits.
        !           735:
        !           736:                         break;
        !           737:                     }
        !           738:                 }
        !           739:             } else {
        !           740:                 grid[i][j] = -1; // Exterior spaces are off limits.
        !           741:             }
        !           742:         }
        !           743:     }
        !           744:     attachRooms(grid, &dungeonProfileCatalog[theProfileIndex], 40, 40);
        !           745:
        !           746:     // Connect to preexisting rooms that were orphaned (mostly preexisting machine rooms).
        !           747:     if (orphanCount > 0) {
        !           748:         pathingGrid = allocGrid();
        !           749:         costGrid = allocGrid();
        !           750:         for (n = 0; n < orphanCount; n++) {
        !           751:
        !           752:             if (D_INSPECT_MACHINES) {
        !           753:                 dumpLevelToScreen();
        !           754:                 copyGrid(pathingGrid, grid);
        !           755:                 findReplaceGrid(pathingGrid, -1, -1, 0);
        !           756:                 hiliteGrid(pathingGrid, &green, 50);
        !           757:                 plotCharWithColor('X', mapToWindowX(orphanList[n][0]), mapToWindowY(orphanList[n][1]), &black, &orange);
        !           758:                 temporaryMessage("Orphan detected:", true);
        !           759:             }
        !           760:
        !           761:             for (i=0; i<DCOLS; i++) {
        !           762:                 for (j=0; j<DROWS; j++) {
        !           763:                     if (interior[i][j]) {
        !           764:                         if (grid[i][j] > 0) {
        !           765:                             pathingGrid[i][j] = 0;
        !           766:                             costGrid[i][j] = 1;
        !           767:                         } else {
        !           768:                             pathingGrid[i][j] = 30000;
        !           769:                             costGrid[i][j] = 1;
        !           770:                         }
        !           771:                     } else {
        !           772:                         pathingGrid[i][j] = 30000;
        !           773:                         costGrid[i][j] = PDS_OBSTRUCTION;
        !           774:                     }
        !           775:                 }
        !           776:             }
        !           777:             dijkstraScan(pathingGrid, costGrid, false);
        !           778:
        !           779:             i = orphanList[n][0];
        !           780:             j = orphanList[n][1];
        !           781:             while (pathingGrid[i][j] > 0) {
        !           782:                 for (dir = 0; dir < 4; dir++) {
        !           783:                     newX = i + nbDirs[dir][0];
        !           784:                     newY = j + nbDirs[dir][1];
        !           785:
        !           786:                     if (coordinatesAreInMap(newX, newY)
        !           787:                         && pathingGrid[newX][newY] < pathingGrid[i][j]) {
        !           788:
        !           789:                         grid[i][j] = 1;
        !           790:                         i = newX;
        !           791:                         j = newY;
        !           792:                         break;
        !           793:                     }
        !           794:                 }
        !           795:                 brogueAssert(dir < 4);
        !           796:                 if (D_INSPECT_MACHINES) {
        !           797:                     dumpLevelToScreen();
        !           798:                     displayGrid(pathingGrid);
        !           799:                     plotCharWithColor('X', mapToWindowX(i), mapToWindowY(j), &black, &orange);
        !           800:                     temporaryMessage("Orphan connecting:", true);
        !           801:                 }
        !           802:             }
        !           803:         }
        !           804:         freeGrid(pathingGrid);
        !           805:         freeGrid(costGrid);
        !           806:     }
        !           807:
        !           808:     addLoops(grid, 10);
        !           809:     for(i=0; i<DCOLS; i++) {
        !           810:         for(j=0; j<DROWS; j++) {
        !           811:             if (interior[i][j]) {
        !           812:                 if (grid[i][j] >= 0) {
        !           813:                     pmap[i][j].layers[SURFACE] = pmap[i][j].layers[GAS] = NOTHING;
        !           814:                 }
        !           815:                 if (grid[i][j] == 0) {
        !           816:                     pmap[i][j].layers[DUNGEON] = GRANITE;
        !           817:                     interior[i][j] = false;
        !           818:                 }
        !           819:                 if (grid[i][j] >= 1) {
        !           820:                     pmap[i][j].layers[DUNGEON] = FLOOR;
        !           821:                 }
        !           822:             }
        !           823:         }
        !           824:     }
        !           825:     freeGrid(grid);
        !           826: }
        !           827:
        !           828: void prepareInteriorWithMachineFlags(char interior[DCOLS][DROWS], short originX, short originY, unsigned long flags, short dungeonProfileIndex) {
        !           829:     short i, j, newX, newY;
        !           830:     enum dungeonLayers layer;
        !           831:     enum directions dir;
        !           832:
        !           833:     // If requested, clear and expand the room as far as possible until either it's convex or it bumps into surrounding rooms
        !           834:     if (flags & BP_MAXIMIZE_INTERIOR) {
        !           835:         expandMachineInterior(interior, 1);
        !           836:     } else if (flags & BP_OPEN_INTERIOR) {
        !           837:         expandMachineInterior(interior, 4);
        !           838:     }
        !           839:
        !           840:     // If requested, cleanse the interior -- no interesting terrain allowed.
        !           841:     if (flags & BP_PURGE_INTERIOR) {
        !           842:         for(i=0; i<DCOLS; i++) {
        !           843:             for(j=0; j<DROWS; j++) {
        !           844:                 if (interior[i][j]) {
        !           845:                     for (layer=0; layer<NUMBER_TERRAIN_LAYERS; layer++) {
        !           846:                         pmap[i][j].layers[layer] = (layer == DUNGEON ? FLOOR : NOTHING);
        !           847:                     }
        !           848:                 }
        !           849:             }
        !           850:         }
        !           851:     }
        !           852:
        !           853:     // If requested, purge pathing blockers -- no traps allowed.
        !           854:     if (flags & BP_PURGE_PATHING_BLOCKERS) {
        !           855:         for(i=0; i<DCOLS; i++) {
        !           856:             for(j=0; j<DROWS; j++) {
        !           857:                 if (interior[i][j]) {
        !           858:                     for (layer=0; layer<NUMBER_TERRAIN_LAYERS; layer++) {
        !           859:                         if (tileCatalog[pmap[i][j].layers[layer]].flags & T_PATHING_BLOCKER) {
        !           860:                             pmap[i][j].layers[layer] = (layer == DUNGEON ? FLOOR : NOTHING);
        !           861:                         }
        !           862:                     }
        !           863:                 }
        !           864:             }
        !           865:         }
        !           866:     }
        !           867:
        !           868:     // If requested, purge the liquid layer in the interior -- no liquids allowed.
        !           869:     if (flags & BP_PURGE_LIQUIDS) {
        !           870:         for(i=0; i<DCOLS; i++) {
        !           871:             for(j=0; j<DROWS; j++) {
        !           872:                 if (interior[i][j]) {
        !           873:                     pmap[i][j].layers[LIQUID] = NOTHING;
        !           874:                 }
        !           875:             }
        !           876:         }
        !           877:     }
        !           878:
        !           879:     // Surround with walls if requested.
        !           880:     if (flags & BP_SURROUND_WITH_WALLS) {
        !           881:         for(i=0; i<DCOLS; i++) {
        !           882:             for(j=0; j<DROWS; j++) {
        !           883:                 if (interior[i][j] && !(pmap[i][j].flags & IS_GATE_SITE)) {
        !           884:                     for (dir=0; dir< DIRECTION_COUNT; dir++) {
        !           885:                         newX = i + nbDirs[dir][0];
        !           886:                         newY = j + nbDirs[dir][1];
        !           887:                         if (coordinatesAreInMap(newX, newY)
        !           888:                             && !interior[newX][newY]
        !           889:                             && !cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)
        !           890:                             && !(pmap[newX][newY].flags & IS_GATE_SITE)
        !           891:                             && !pmap[newX][newY].machineNumber
        !           892:                             && cellHasTerrainFlag(newX, newY, T_PATHING_BLOCKER)) {
        !           893:                             for (layer=0; layer<NUMBER_TERRAIN_LAYERS; layer++) {
        !           894:                                 pmap[newX][newY].layers[layer] = (layer == DUNGEON ? WALL : 0);
        !           895:                             }
        !           896:                         }
        !           897:                     }
        !           898:                 }
        !           899:             }
        !           900:         }
        !           901:     }
        !           902:
        !           903:     // Completely clear the interior, fill with granite, and cut entirely new rooms into it from the gate site.
        !           904:     // Then zero out any portion of the interior that is still wall.
        !           905:     if (flags & BP_REDESIGN_INTERIOR) {
        !           906:         redesignInterior(interior, originX, originY, dungeonProfileIndex);
        !           907:     }
        !           908:
        !           909:     // Reinforce surrounding tiles and interior tiles if requested to prevent tunneling in or through.
        !           910:     if (flags & BP_IMPREGNABLE) {
        !           911:         for(i=0; i<DCOLS; i++) {
        !           912:             for(j=0; j<DROWS; j++) {
        !           913:                 if (interior[i][j]
        !           914:                     && !(pmap[i][j].flags & IS_GATE_SITE)) {
        !           915:
        !           916:                     pmap[i][j].flags |= IMPREGNABLE;
        !           917:                     for (dir=0; dir< DIRECTION_COUNT; dir++) {
        !           918:                         newX = i + nbDirs[dir][0];
        !           919:                         newY = j + nbDirs[dir][1];
        !           920:                         if (coordinatesAreInMap(newX, newY)
        !           921:                             && !interior[newX][newY]
        !           922:                             && !(pmap[newX][newY].flags & IS_GATE_SITE)) {
        !           923:
        !           924:                             pmap[newX][newY].flags |= IMPREGNABLE;
        !           925:                         }
        !           926:                     }
        !           927:                 }
        !           928:             }
        !           929:         }
        !           930:     }
        !           931: }
        !           932:
        !           933: typedef struct machineData {
        !           934:     // Our boolean grids:
        !           935:     char interior[DCOLS][DROWS];    // This is the master grid for the machine. All area inside the machine are set to true.
        !           936:     char occupied[DCOLS][DROWS];    // This keeps track of what is within the personal space of a previously built feature in the same machine.
        !           937:     char candidates[DCOLS][DROWS];  // This is calculated at the start of each feature, and is true where that feature is eligible for building.
        !           938:     char blockingMap[DCOLS][DROWS]; // Used during terrain/DF placement in features that are flagged not to tolerate blocking, to see if they block.
        !           939:     char viewMap[DCOLS][DROWS];     // Used for features with MF_IN_VIEW_OF_ORIGIN, to calculate which cells are in view of the origin.
        !           940:
        !           941:     pcell levelBackup[DCOLS][DROWS];
        !           942:
        !           943:     item *spawnedItems[MACHINES_BUFFER_LENGTH];
        !           944:     item *spawnedItemsSub[MACHINES_BUFFER_LENGTH];
        !           945:     creature *spawnedMonsters[MACHINES_BUFFER_LENGTH];
        !           946:     creature *spawnedMonstersSub[MACHINES_BUFFER_LENGTH];
        !           947:
        !           948:     short gateCandidates[50][2];
        !           949:     short distances[100];
        !           950:     short sRows[DROWS];
        !           951:     short sCols[DCOLS];
        !           952: } machineData;
        !           953:
        !           954: // Returns true if the machine got built; false if it was aborted.
        !           955: // If empty array parentSpawnedItems or parentSpawnedMonsters is given, will pass those back for deletion if necessary.
        !           956: boolean buildAMachine(enum machineTypes bp,
        !           957:                       short originX, short originY,
        !           958:                       unsigned long requiredMachineFlags,
        !           959:                       item *adoptiveItem,
        !           960:                       item *parentSpawnedItems[MACHINES_BUFFER_LENGTH],
        !           961:                       creature *parentSpawnedMonsters[MACHINES_BUFFER_LENGTH]) {
        !           962:
        !           963:     short i, j, k, feat, randIndex, totalFreq, instance, instanceCount = 0,
        !           964:         featX, featY, itemCount, monsterCount, qualifyingTileCount,
        !           965:         **distanceMap = NULL, distance25, distance75, distanceBound[2],
        !           966:         personalSpace, failsafe, locationFailsafe,
        !           967:         machineNumber;
        !           968:
        !           969:     const unsigned long alternativeFlags[2] = {MF_ALTERNATIVE, MF_ALTERNATIVE_2};
        !           970:
        !           971:     boolean DFSucceeded, terrainSucceeded, generateEverywhere, chooseBP,
        !           972:         chooseLocation, tryAgain, success = false, skipFeature[20];
        !           973:
        !           974:     creature *monst = NULL, *nextMonst, *torchBearer = NULL, *leader = NULL;
        !           975:
        !           976:     item *theItem = NULL, *torch = NULL;
        !           977:
        !           978:     const machineFeature *feature;
        !           979:
        !           980:     machineData *p = malloc(sizeof(machineData));
        !           981:
        !           982:     memset(p, 0, sizeof(machineData));
        !           983:
        !           984:     chooseBP = (((signed short) bp) <= 0 ? true : false);
        !           985:
        !           986:     chooseLocation = (originX <= 0 || originY <= 0 ? true : false);
        !           987:
        !           988:     failsafe = 10;
        !           989:     do {
        !           990:         tryAgain = false;
        !           991:         if (--failsafe <= 0) {
        !           992:             if (distanceMap) {
        !           993:                 freeGrid(distanceMap);
        !           994:             }
        !           995:             if (D_MESSAGE_MACHINE_GENERATION) {
        !           996:                 if (chooseBP || chooseLocation) {
        !           997:                     printf("\nDepth %i: Failed to build a machine; gave up after 10 unsuccessful attempts to find a suitable blueprint and/or location.",
        !           998:                            rogue.depthLevel);
        !           999:                 } else {
        !          1000:                     printf("\nDepth %i: Failed to build a machine; requested blueprint and location did not work.",
        !          1001:                            rogue.depthLevel);
        !          1002:                 }
        !          1003:             }
        !          1004:             free(p);
        !          1005:             return false;
        !          1006:         }
        !          1007:
        !          1008:         if (chooseBP) { // If no blueprint is given, then pick one:
        !          1009:
        !          1010:             // First, choose the blueprint. We choose from among blueprints
        !          1011:             // that have the required blueprint flags and that satisfy the depth requirements.
        !          1012:             totalFreq = 0;
        !          1013:             for (i=1; i<NUMBER_BLUEPRINTS; i++) {
        !          1014:                 if (blueprintQualifies(i, requiredMachineFlags)) {
        !          1015:                     totalFreq += blueprintCatalog[i].frequency;
        !          1016:                 }
        !          1017:             }
        !          1018:
        !          1019:             if (!totalFreq) { // If no suitable blueprints are in the library, fail.
        !          1020:                 if (distanceMap) {
        !          1021:                     freeGrid(distanceMap);
        !          1022:                 }
        !          1023:                 if (D_MESSAGE_MACHINE_GENERATION) printf("\nDepth %i: Failed to build a machine because no suitable blueprints were available.",
        !          1024:                              rogue.depthLevel);
        !          1025:                 free(p);
        !          1026:                 return false;
        !          1027:             }
        !          1028:
        !          1029:             // Pick from among the suitable blueprints.
        !          1030:             randIndex = rand_range(1, totalFreq);
        !          1031:             for (i=1; i<NUMBER_BLUEPRINTS; i++) {
        !          1032:                 if (blueprintQualifies(i, requiredMachineFlags)) {
        !          1033:                     if (randIndex <= blueprintCatalog[i].frequency) {
        !          1034:                         bp = i;
        !          1035:                         break;
        !          1036:                     } else {
        !          1037:                         randIndex -= blueprintCatalog[i].frequency;
        !          1038:                     }
        !          1039:                 }
        !          1040:             }
        !          1041:
        !          1042:             // If we don't have a blueprint yet, something went wrong.
        !          1043:             brogueAssert(bp>0);
        !          1044:         }
        !          1045:
        !          1046:         // Find a location and map out the machine interior.
        !          1047:         if (blueprintCatalog[bp].flags & BP_ROOM) {
        !          1048:             // If it's a room machine, count up the gates of appropriate
        !          1049:             // choke size and remember where they are. The origin of the room will be the gate location.
        !          1050:             zeroOutGrid(p->interior);
        !          1051:
        !          1052:             if (chooseLocation) {
        !          1053:                 analyzeMap(true); // Make sure the chokeMap is up to date.
        !          1054:                 totalFreq = 0;
        !          1055:                 for(i=0; i<DCOLS; i++) {
        !          1056:                     for(j=0; j<DROWS && totalFreq < 50; j++) {
        !          1057:                         if ((pmap[i][j].flags & IS_GATE_SITE)
        !          1058:                             && !(pmap[i][j].flags & IS_IN_MACHINE)
        !          1059:                             && chokeMap[i][j] >= blueprintCatalog[bp].roomSize[0]
        !          1060:                             && chokeMap[i][j] <= blueprintCatalog[bp].roomSize[1]) {
        !          1061:
        !          1062:                             //DEBUG printf("\nDepth %i: Gate site qualified with interior size of %i.", rogue.depthLevel, chokeMap[i][j]);
        !          1063:                             p->gateCandidates[totalFreq][0] = i;
        !          1064:                             p->gateCandidates[totalFreq][1] = j;
        !          1065:                             totalFreq++;
        !          1066:                         }
        !          1067:                     }
        !          1068:                 }
        !          1069:
        !          1070:                 if (totalFreq) {
        !          1071:                     // Choose the gate.
        !          1072:                     randIndex = rand_range(0, totalFreq - 1);
        !          1073:                     originX = p->gateCandidates[randIndex][0];
        !          1074:                     originY = p->gateCandidates[randIndex][1];
        !          1075:                 } else {
        !          1076:                     // If no suitable sites, abort.
        !          1077:                     if (distanceMap) {
        !          1078:                         freeGrid(distanceMap);
        !          1079:                     }
        !          1080:                     if (D_MESSAGE_MACHINE_GENERATION) printf("\nDepth %i: Failed to build a machine; there was no eligible door candidate for the chosen room machine from blueprint %i.",
        !          1081:                                  rogue.depthLevel,
        !          1082:                                  bp);
        !          1083:                     free(p);
        !          1084:                     return false;
        !          1085:                 }
        !          1086:             }
        !          1087:
        !          1088:             // Now map out the interior into interior[][].
        !          1089:             // Start at the gate location and do a depth-first floodfill to grab all adjoining tiles with the
        !          1090:             // same or lower choke value, ignoring any tiles that are already part of a machine.
        !          1091:             // If we get false from this, try again. If we've tried too many times already, abort.
        !          1092:             tryAgain = !addTileToMachineInteriorAndIterate(p->interior, originX, originY);
        !          1093:         } else if (blueprintCatalog[bp].flags & BP_VESTIBULE) {
        !          1094:             if (chooseLocation) {
        !          1095:                 // Door machines must have locations passed in. We can't pick one ourselves.
        !          1096:                 if (distanceMap) {
        !          1097:                     freeGrid(distanceMap);
        !          1098:                 }
        !          1099:                 if (D_MESSAGE_MACHINE_GENERATION) printf("\nDepth %i: ERROR: Attempted to build a door machine from blueprint %i without a location being provided.",
        !          1100:                              rogue.depthLevel,
        !          1101:                              bp);
        !          1102:                 free(p);
        !          1103:                 return false;
        !          1104:             }
        !          1105:             if (!fillInteriorForVestibuleMachine(p->interior, bp, originX, originY)) {
        !          1106:                 if (distanceMap) {
        !          1107:                     freeGrid(distanceMap);
        !          1108:                 }
        !          1109:                 if (D_MESSAGE_MACHINE_GENERATION) printf("\nDepth %i: Failed to build a door machine from blueprint %i; not enough room.",
        !          1110:                              rogue.depthLevel,
        !          1111:                              bp);
        !          1112:                 free(p);
        !          1113:                 return false;
        !          1114:             }
        !          1115:         } else {
        !          1116:             // Find a location and map out the interior for a non-room machine.
        !          1117:             // The strategy here is simply to pick a random location on the map,
        !          1118:             // expand it along a pathing map by one space in all directions until the size reaches
        !          1119:             // the chosen size, and then make sure the resulting space qualifies.
        !          1120:             // If not, try again. If we've tried too many times already, abort.
        !          1121:
        !          1122:             locationFailsafe = 10;
        !          1123:             do {
        !          1124:                 zeroOutGrid(p->interior);
        !          1125:                 tryAgain = false;
        !          1126:
        !          1127:                 if (chooseLocation) {
        !          1128:                     // Pick a random origin location.
        !          1129:                     randomMatchingLocation(&originX, &originY, FLOOR, NOTHING, -1);
        !          1130:                 }
        !          1131:
        !          1132:                 if (!distanceMap) {
        !          1133:                     distanceMap = allocGrid();
        !          1134:                 }
        !          1135:                 fillGrid(distanceMap, 0);
        !          1136:                 calculateDistances(distanceMap, originX, originY, T_PATHING_BLOCKER, NULL, true, false);
        !          1137:                 qualifyingTileCount = 0; // Keeps track of how many interior cells we've added.
        !          1138:                 totalFreq = rand_range(blueprintCatalog[bp].roomSize[0], blueprintCatalog[bp].roomSize[1]); // Keeps track of the goal size.
        !          1139:
        !          1140:                 fillSequentialList(p->sCols, DCOLS);
        !          1141:                 shuffleList(p->sCols, DCOLS);
        !          1142:                 fillSequentialList(p->sRows, DROWS);
        !          1143:                 shuffleList(p->sRows, DROWS);
        !          1144:
        !          1145:                 for (k=0; k<1000 && qualifyingTileCount < totalFreq; k++) {
        !          1146:                     for(i=0; i<DCOLS && qualifyingTileCount < totalFreq; i++) {
        !          1147:                         for(j=0; j<DROWS && qualifyingTileCount < totalFreq; j++) {
        !          1148:                             if (distanceMap[p->sCols[i]][p->sRows[j]] == k) {
        !          1149:                                 p->interior[p->sCols[i]][p->sRows[j]] = true;
        !          1150:                                 qualifyingTileCount++;
        !          1151:
        !          1152:                                 if (pmap[p->sCols[i]][p->sRows[j]].flags & (HAS_ITEM | HAS_MONSTER | IS_IN_MACHINE)) {
        !          1153:                                     // Abort if we've entered another machine or engulfed another machine's item or monster.
        !          1154:                                     tryAgain = true;
        !          1155:                                     qualifyingTileCount = totalFreq; // This is a hack to drop out of these three for-loops.
        !          1156:                                 }
        !          1157:                             }
        !          1158:                         }
        !          1159:                     }
        !          1160:                 }
        !          1161:
        !          1162:                 // Now make sure the interior map satisfies the machine's qualifications.
        !          1163:                 if ((blueprintCatalog[bp].flags & BP_TREAT_AS_BLOCKING)
        !          1164:                     && levelIsDisconnectedWithBlockingMap(p->interior, false)) {
        !          1165:                     tryAgain = true;
        !          1166:                 } else if ((blueprintCatalog[bp].flags & BP_REQUIRE_BLOCKING)
        !          1167:                            && levelIsDisconnectedWithBlockingMap(p->interior, true) < 100) {
        !          1168:                     tryAgain = true; // BP_REQUIRE_BLOCKING needs some work to make sure the disconnect is interesting.
        !          1169:                 }
        !          1170:                 // If locationFailsafe runs out, tryAgain will still be true, and we'll try a different machine.
        !          1171:                 // If we're not choosing the blueprint, then don't bother with the locationFailsafe; just use the higher-level failsafe.
        !          1172:             } while (chooseBP && tryAgain && --locationFailsafe);
        !          1173:         }
        !          1174:
        !          1175:         // If something went wrong, but we haven't been charged with choosing blueprint OR location,
        !          1176:         // then there is nothing to try again, so just fail.
        !          1177:         if (tryAgain && !chooseBP && !chooseLocation) {
        !          1178:             if (distanceMap) {
        !          1179:                 freeGrid(distanceMap);
        !          1180:             }
        !          1181:             free(p);
        !          1182:             return false;
        !          1183:         }
        !          1184:
        !          1185:         // Now loop if necessary.
        !          1186:     } while (tryAgain);
        !          1187:
        !          1188:     // This is the point of no return. Back up the level so it can be restored if we have to abort this machine after this point.
        !          1189:     copyMap(pmap, p->levelBackup);
        !          1190:
        !          1191:     // Perform any transformations to the interior indicated by the blueprint flags, including expanding the interior if requested.
        !          1192:     prepareInteriorWithMachineFlags(p->interior, originX, originY, blueprintCatalog[bp].flags, blueprintCatalog[bp].dungeonProfileType);
        !          1193:
        !          1194:     // If necessary, label the interior as IS_IN_AREA_MACHINE or IS_IN_ROOM_MACHINE and mark down the number.
        !          1195:     machineNumber = ++rogue.machineNumber; // Reserve this machine number, starting with 1.
        !          1196:     for(i=0; i<DCOLS; i++) {
        !          1197:         for(j=0; j<DROWS; j++) {
        !          1198:             if (p->interior[i][j]) {
        !          1199:                 pmap[i][j].flags |= ((blueprintCatalog[bp].flags & BP_ROOM) ? IS_IN_ROOM_MACHINE : IS_IN_AREA_MACHINE);
        !          1200:                 pmap[i][j].machineNumber = machineNumber;
        !          1201:                 // also clear any secret doors, since they screw up distance mapping and aren't fun inside machines
        !          1202:                 if (pmap[i][j].layers[DUNGEON] == SECRET_DOOR) {
        !          1203:                     pmap[i][j].layers[DUNGEON] = DOOR;
        !          1204:                 }
        !          1205:             }
        !          1206:         }
        !          1207:     }
        !          1208:
        !          1209: //  DEBUG printf("\n\nWorking on blueprint %i, with origin at (%i, %i). Here's the initial interior map:", bp, originX, originY);
        !          1210: //  DEBUG logBuffer(interior);
        !          1211:
        !          1212:     // Calculate the distance map (so that features that want to be close to or far from the origin can be placed accordingly)
        !          1213:     // and figure out the 33rd and 67th percentiles for features that want to be near or far from the origin.
        !          1214:     if (!distanceMap) {
        !          1215:         distanceMap = allocGrid();
        !          1216:     }
        !          1217:     fillGrid(distanceMap, 0);
        !          1218:     calculateDistances(distanceMap, originX, originY, T_PATHING_BLOCKER, NULL, true, true);
        !          1219:     qualifyingTileCount = 0;
        !          1220:     for (i=0; i<100; i++) {
        !          1221:         p->distances[i] = 0;
        !          1222:     }
        !          1223:     for(i=0; i<DCOLS; i++) {
        !          1224:         for(j=0; j<DROWS; j++) {
        !          1225:             if (p->interior[i][j]
        !          1226:                 && distanceMap[i][j] < 100) {
        !          1227:                 p->distances[distanceMap[i][j]]++; // create a histogram of distances -- poor man's sort function
        !          1228:                 qualifyingTileCount++;
        !          1229:             }
        !          1230:         }
        !          1231:     }
        !          1232:     distance25 = (int) (qualifyingTileCount / 4);
        !          1233:     distance75 = (int) (3 * qualifyingTileCount / 4);
        !          1234:     for (i=0; i<100; i++) {
        !          1235:         if (distance25 <= p->distances[i]) {
        !          1236:             distance25 = i;
        !          1237:             break;
        !          1238:         } else {
        !          1239:             distance25 -= p->distances[i];
        !          1240:         }
        !          1241:     }
        !          1242:     for (i=0; i<100; i++) {
        !          1243:         if (distance75 <= p->distances[i]) {
        !          1244:             distance75 = i;
        !          1245:             break;
        !          1246:         } else {
        !          1247:             distance75 -= p->distances[i];
        !          1248:         }
        !          1249:     }
        !          1250:     //DEBUG printf("\nDistances calculated: 33rd percentile of distance is %i, and 67th is %i.", distance25, distance75);
        !          1251:
        !          1252:     // Now decide which features will be skipped -- of the features marked MF_ALTERNATIVE, skip all but one, chosen randomly.
        !          1253:     // Then repeat and do the same with respect to MF_ALTERNATIVE_2, to provide up to two independent sets of alternative features per machine.
        !          1254:
        !          1255:     for (i=0; i<blueprintCatalog[bp].featureCount; i++) {
        !          1256:         skipFeature[i] = false;
        !          1257:     }
        !          1258:     for (j = 0; j <= 1; j++) {
        !          1259:         totalFreq = 0;
        !          1260:         for (i=0; i<blueprintCatalog[bp].featureCount; i++) {
        !          1261:             if (blueprintCatalog[bp].feature[i].flags & alternativeFlags[j]) {
        !          1262:                 skipFeature[i] = true;
        !          1263:                 totalFreq++;
        !          1264:             }
        !          1265:         }
        !          1266:         if (totalFreq > 0) {
        !          1267:             randIndex = rand_range(1, totalFreq);
        !          1268:             for (i=0; i<blueprintCatalog[bp].featureCount; i++) {
        !          1269:                 if (blueprintCatalog[bp].feature[i].flags & alternativeFlags[j]) {
        !          1270:                     if (randIndex == 1) {
        !          1271:                         skipFeature[i] = false; // This is the alternative that gets built. The rest do not.
        !          1272:                         break;
        !          1273:                     } else {
        !          1274:                         randIndex--;
        !          1275:                     }
        !          1276:                 }
        !          1277:             }
        !          1278:         }
        !          1279:     }
        !          1280:
        !          1281:     // Keep track of all monsters and items that we spawn -- if we abort, we have to go back and delete them all.
        !          1282:     itemCount = monsterCount = 0;
        !          1283:
        !          1284:     // Zero out occupied[][], and use it to keep track of the personal space around each feature that gets placed.
        !          1285:     zeroOutGrid(p->occupied);
        !          1286:
        !          1287:     // Now tick through the features and build them.
        !          1288:     for (feat = 0; feat < blueprintCatalog[bp].featureCount; feat++) {
        !          1289:
        !          1290:         if (skipFeature[feat]) {
        !          1291:             continue; // Skip the alternative features that were not selected for building.
        !          1292:         }
        !          1293:
        !          1294:         feature = &(blueprintCatalog[bp].feature[feat]);
        !          1295:
        !          1296:         // Figure out the distance bounds.
        !          1297:         distanceBound[0] = 0;
        !          1298:         distanceBound[1] = 10000;
        !          1299:         if (feature->flags & MF_NEAR_ORIGIN) {
        !          1300:             distanceBound[1] = distance25;
        !          1301:         }
        !          1302:         if (feature->flags & MF_FAR_FROM_ORIGIN) {
        !          1303:             distanceBound[0] = distance75;
        !          1304:         }
        !          1305:
        !          1306:         if (feature->flags & (MF_IN_VIEW_OF_ORIGIN | MF_IN_PASSABLE_VIEW_OF_ORIGIN)) {
        !          1307:             zeroOutGrid(p->viewMap);
        !          1308:             if (feature->flags & MF_IN_PASSABLE_VIEW_OF_ORIGIN) {
        !          1309:                 getFOVMask(p->viewMap, originX, originY, max(DCOLS, DROWS) * FP_FACTOR, T_PATHING_BLOCKER, 0, false);
        !          1310:             } else {
        !          1311:                 getFOVMask(p->viewMap, originX, originY, max(DCOLS, DROWS) * FP_FACTOR, (T_OBSTRUCTS_PASSABILITY | T_OBSTRUCTS_VISION), 0, false);
        !          1312:             }
        !          1313:             p->viewMap[originX][originY] = true;
        !          1314:
        !          1315:             if (D_INSPECT_MACHINES) {
        !          1316:                 dumpLevelToScreen();
        !          1317:                 hiliteCharGrid(p->viewMap, &omniscienceColor, 75);
        !          1318:                 temporaryMessage("Showing visibility.", true);
        !          1319:             }
        !          1320:         }
        !          1321:
        !          1322:         do { // If the MF_REPEAT_UNTIL_NO_PROGRESS flag is set, repeat until we fail to build the required number of instances.
        !          1323:
        !          1324:             // Make a master map of candidate locations for this feature.
        !          1325:             qualifyingTileCount = 0;
        !          1326:             for(i=0; i<DCOLS; i++) {
        !          1327:                 for(j=0; j<DROWS; j++) {
        !          1328:                     if (cellIsFeatureCandidate(i, j,
        !          1329:                                                originX, originY,
        !          1330:                                                distanceBound,
        !          1331:                                                p->interior, p->occupied, p->viewMap, distanceMap,
        !          1332:                                                machineNumber, feature->flags, blueprintCatalog[bp].flags)) {
        !          1333:                         qualifyingTileCount++;
        !          1334:                         p->candidates[i][j] = true;
        !          1335:                     } else {
        !          1336:                         p->candidates[i][j] = false;
        !          1337:                     }
        !          1338:                 }
        !          1339:             }
        !          1340:
        !          1341:             if (D_INSPECT_MACHINES) {
        !          1342:                 dumpLevelToScreen();
        !          1343:                 hiliteCharGrid(p->occupied, &red, 75);
        !          1344:                 hiliteCharGrid(p->candidates, &green, 75);
        !          1345:                 hiliteCharGrid(p->interior, &blue, 75);
        !          1346:                 temporaryMessage("Indicating: Occupied (red); Candidates (green); Interior (blue).", true);
        !          1347:             }
        !          1348:
        !          1349:             if (feature->flags & MF_EVERYWHERE & ~MF_BUILD_AT_ORIGIN) {
        !          1350:                 // Generate everywhere that qualifies -- instead of randomly picking tiles, keep spawning until we run out of eligible tiles.
        !          1351:                 generateEverywhere = true;
        !          1352:             } else {
        !          1353:                 // build as many instances as required
        !          1354:                 generateEverywhere = false;
        !          1355:                 instanceCount = rand_range(feature->instanceCountRange[0], feature->instanceCountRange[1]);
        !          1356:             }
        !          1357:
        !          1358:             // Cache the personal space constant.
        !          1359:             personalSpace = feature->personalSpace;
        !          1360:
        !          1361:             for (instance = 0; (generateEverywhere || instance < instanceCount) && qualifyingTileCount > 0;) {
        !          1362:
        !          1363:                 // Find a location for the feature.
        !          1364:                 if (feature->flags & MF_BUILD_AT_ORIGIN) {
        !          1365:                     // Does the feature want to be at the origin? If so, put it there. (Just an optimization.)
        !          1366:                     featX = originX;
        !          1367:                     featY = originY;
        !          1368:                 } else {
        !          1369:                     // Pick our candidate location randomly, and also strike it from
        !          1370:                     // the candidates map so that subsequent instances of this same feature can't choose it.
        !          1371:                     featX = -1;
        !          1372:                     featY = -1;
        !          1373:                     randIndex = rand_range(1, qualifyingTileCount);
        !          1374:                     for(i=0; i<DCOLS && featX < 0; i++) {
        !          1375:                         for(j=0; j<DROWS && featX < 0; j++) {
        !          1376:                             if (p->candidates[i][j]) {
        !          1377:                                 if (randIndex == 1) {
        !          1378:                                     // This is the place!
        !          1379:                                     featX = i;
        !          1380:                                     featY = j;
        !          1381:                                     i = DCOLS;  // break out of the loops
        !          1382:                                     j = DROWS;
        !          1383:                                 } else {
        !          1384:                                     randIndex--;
        !          1385:                                 }
        !          1386:                             }
        !          1387:                         }
        !          1388:                     }
        !          1389:                 }
        !          1390:                 // Don't waste time trying the same place again whether or not this attempt succeeds.
        !          1391:                 p->candidates[featX][featY] = false;
        !          1392:                 qualifyingTileCount--;
        !          1393:
        !          1394:                 DFSucceeded = terrainSucceeded = true;
        !          1395:
        !          1396:                 // Try to build the DF first, if any, since we don't want it to be disrupted by subsequently placed terrain.
        !          1397:                 if (feature->featureDF) {
        !          1398:                     DFSucceeded = spawnDungeonFeature(featX, featY, &dungeonFeatureCatalog[feature->featureDF], false,
        !          1399:                                                       !(feature->flags & MF_PERMIT_BLOCKING));
        !          1400:                 }
        !          1401:
        !          1402:                 // Now try to place the terrain tile, if any.
        !          1403:                 if (DFSucceeded && feature->terrain) {
        !          1404:                     // Must we check for blocking?
        !          1405:                     if (!(feature->flags & MF_PERMIT_BLOCKING)
        !          1406:                         && ((tileCatalog[feature->terrain].flags & T_PATHING_BLOCKER) || (feature->flags & MF_TREAT_AS_BLOCKING))) {
        !          1407:                         // Yes, check for blocking.
        !          1408:
        !          1409:                         zeroOutGrid(p->blockingMap);
        !          1410:                         p->blockingMap[featX][featY] = true;
        !          1411:                         terrainSucceeded = !levelIsDisconnectedWithBlockingMap(p->blockingMap, false);
        !          1412:                     }
        !          1413:                     if (terrainSucceeded) {
        !          1414:                         pmap[featX][featY].layers[feature->layer] = feature->terrain;
        !          1415:                     }
        !          1416:                 }
        !          1417:
        !          1418:                 // OK, if placement was successful, clear some personal space around the feature so subsequent features can't be generated too close.
        !          1419:                 // Personal space of 0 means nothing gets cleared, 1 means that only the tile itself gets cleared, and 2 means the 3x3 grid centered on it.
        !          1420:
        !          1421:                 if (DFSucceeded && terrainSucceeded) {
        !          1422:                     for (i = featX - personalSpace + 1;
        !          1423:                          i <= featX + personalSpace - 1;
        !          1424:                          i++) {
        !          1425:                         for (j = featY - personalSpace + 1;
        !          1426:                              j <= featY + personalSpace - 1;
        !          1427:                              j++) {
        !          1428:                             if (coordinatesAreInMap(i, j)) {
        !          1429:                                 if (p->candidates[i][j]) {
        !          1430:                                     brogueAssert(!occupied[i][j] || (i == originX && j == originY)); // Candidates[][] should never be true where occupied[][] is true.
        !          1431:                                     p->candidates[i][j] = false;
        !          1432:                                     qualifyingTileCount--;
        !          1433:                                 }
        !          1434:                                 p->occupied[i][j] = true;
        !          1435:                             }
        !          1436:                         }
        !          1437:                     }
        !          1438:                     instance++; // we've placed an instance
        !          1439:                     //DEBUG printf("\nPlaced instance #%i of feature %i at (%i, %i).", instance, feat, featX, featY);
        !          1440:                 }
        !          1441:
        !          1442:                 if (DFSucceeded && terrainSucceeded) { // Proceed only if the terrain stuff for this instance succeeded.
        !          1443:
        !          1444:                     theItem = NULL;
        !          1445:
        !          1446:                     // Mark the feature location as part of the machine, in case it is not already inside of it.
        !          1447:                     pmap[featX][featY].flags |= ((blueprintCatalog[bp].flags & BP_ROOM) ? IS_IN_ROOM_MACHINE : IS_IN_AREA_MACHINE);
        !          1448:                     pmap[featX][featY].machineNumber = machineNumber;
        !          1449:
        !          1450:                     // Mark the feature location as impregnable if requested.
        !          1451:                     if (feature->flags & MF_IMPREGNABLE) {
        !          1452:                         pmap[featX][featY].flags |= IMPREGNABLE;
        !          1453:                     }
        !          1454:
        !          1455:                     // Generate an item as necessary.
        !          1456:                     if ((feature->flags & MF_GENERATE_ITEM)
        !          1457:                         || (adoptiveItem && (feature->flags & MF_ADOPT_ITEM) && (blueprintCatalog[bp].flags & BP_ADOPT_ITEM))) {
        !          1458:                         // Are we adopting an item instead of generating one?
        !          1459:                         if (adoptiveItem && (feature->flags & MF_ADOPT_ITEM) && (blueprintCatalog[bp].flags & BP_ADOPT_ITEM)) {
        !          1460:                             theItem = adoptiveItem;
        !          1461:                             adoptiveItem = NULL; // can be adopted only once
        !          1462:                         } else {
        !          1463:                             // Have to create an item ourselves.
        !          1464:                             theItem = generateItem(feature->itemCategory, feature->itemKind);
        !          1465:                             failsafe = 1000;
        !          1466:                             while ((theItem->flags & ITEM_CURSED)
        !          1467:                                    || ((feature->flags & MF_REQUIRE_GOOD_RUNIC) && (!(theItem->flags & ITEM_RUNIC))) // runic if requested
        !          1468:                                    || ((feature->flags & MF_NO_THROWING_WEAPONS) && theItem->category == WEAPON && theItem->quantity > 1) // no throwing weapons if prohibited
        !          1469:                                    || itemIsADuplicate(theItem, p->spawnedItems, itemCount)) { // don't want to duplicates of rings, staffs, etc.
        !          1470:                                 deleteItem(theItem);
        !          1471:                                 theItem = generateItem(feature->itemCategory, feature->itemKind);
        !          1472:                                 if (failsafe <= 0) {
        !          1473:                                     break;
        !          1474:                                 }
        !          1475:                                 failsafe--;
        !          1476:                             }
        !          1477:                             p->spawnedItems[itemCount] = theItem; // Keep a list of generated items so that we can delete them all if construction fails.
        !          1478:                             itemCount++;
        !          1479:                         }
        !          1480:                         theItem->flags |= feature->itemFlags;
        !          1481:
        !          1482:                         addLocationToKey(theItem, featX, featY, (feature->flags & MF_KEY_DISPOSABLE) ? true : false);
        !          1483:                         theItem->originDepth = rogue.depthLevel;
        !          1484:                         if (feature->flags & MF_SKELETON_KEY) {
        !          1485:                             addMachineNumberToKey(theItem, machineNumber, (feature->flags & MF_KEY_DISPOSABLE) ? true : false);
        !          1486:                         }
        !          1487:                         if (!(feature->flags & MF_OUTSOURCE_ITEM_TO_MACHINE)
        !          1488:                             && !(feature->flags & MF_MONSTER_TAKE_ITEM)) {
        !          1489:                             // Place the item at the feature location.
        !          1490:                             placeItem(theItem, featX, featY);
        !          1491:                         }
        !          1492:                     }
        !          1493:
        !          1494:                     if (feature->flags & (MF_OUTSOURCE_ITEM_TO_MACHINE | MF_BUILD_VESTIBULE)) {
        !          1495:                         // Put this item up for adoption, or generate a door guard machine.
        !          1496:                         // Try to create a sub-machine that qualifies.
        !          1497:                         // If we fail 10 times, abort the entire machine (including any sub-machines already built).
        !          1498:                         // Also, if we build a sub-machine, and it succeeds, but this (its parent machine) fails,
        !          1499:                         // we pass the monsters and items that it spawned back to the parent,
        !          1500:                         // so that if the parent fails, they can all be freed.
        !          1501:                         for (i=10; i > 0; i--) {
        !          1502:                             // First make sure our adopted item, if any, is not on the floor or in the pack already.
        !          1503:                             // Otherwise, a previous attempt to place it may have put it on the floor in a different
        !          1504:                             // machine, only to have that machine fail and be deleted, leaving the item remaining on
        !          1505:                             // the floor where placed.
        !          1506:                             if ((feature->flags & MF_OUTSOURCE_ITEM_TO_MACHINE) && theItem) {
        !          1507:                                 removeItemFromChain(theItem, floorItems);
        !          1508:                                 removeItemFromChain(theItem, packItems);
        !          1509:                                 theItem->nextItem = NULL;
        !          1510:                                 success = buildAMachine(-1, -1, -1, BP_ADOPT_ITEM, theItem, p->spawnedItemsSub, p->spawnedMonstersSub);
        !          1511:                             } else if (feature->flags & MF_BUILD_VESTIBULE) {
        !          1512:                                 success = buildAMachine(-1, featX, featY, BP_VESTIBULE, NULL, p->spawnedItemsSub, p->spawnedMonstersSub);
        !          1513:                             }
        !          1514:
        !          1515:                             // Now put the item up for adoption.
        !          1516:                             if (success) {
        !          1517:                                 // Success! Now we have to add that machine's items and monsters to our own list, so they
        !          1518:                                 // all get deleted if this machine or its parent fails.
        !          1519:                                 for (j=0; j<MACHINES_BUFFER_LENGTH && p->spawnedItemsSub[j]; j++) {
        !          1520:                                     p->spawnedItems[itemCount] = p->spawnedItemsSub[j];
        !          1521:                                     itemCount++;
        !          1522:                                     p->spawnedItemsSub[j] = NULL;
        !          1523:                                 }
        !          1524:                                 for (j=0; j<MACHINES_BUFFER_LENGTH && p->spawnedMonstersSub[j]; j++) {
        !          1525:                                     p->spawnedMonsters[monsterCount] = p->spawnedMonstersSub[j];
        !          1526:                                     monsterCount++;
        !          1527:                                     p->spawnedMonstersSub[j] = NULL;
        !          1528:                                 }
        !          1529:                                 break;
        !          1530:                             }
        !          1531:                         }
        !          1532:
        !          1533:                         if (!i) {
        !          1534:                             if (D_MESSAGE_MACHINE_GENERATION) printf("\nDepth %i: Failed to place blueprint %i because it requires an adoptive machine and we couldn't place one.", rogue.depthLevel, bp);
        !          1535:                             // failure! abort!
        !          1536:                             copyMap(p->levelBackup, pmap);
        !          1537:                             abortItemsAndMonsters(p->spawnedItems, p->spawnedMonsters);
        !          1538:                             freeGrid(distanceMap);
        !          1539:                             free(p);
        !          1540:                             return false;
        !          1541:                         }
        !          1542:                         theItem = NULL;
        !          1543:                     }
        !          1544:
        !          1545:                     // Generate a horde as necessary.
        !          1546:                     if ((feature->flags & MF_GENERATE_HORDE)
        !          1547:                         || feature->monsterID) {
        !          1548:
        !          1549:                         if (feature->flags & MF_GENERATE_HORDE) {
        !          1550:                             monst = spawnHorde(0,
        !          1551:                                                featX,
        !          1552:                                                featY,
        !          1553:                                                ((HORDE_IS_SUMMONED | HORDE_LEADER_CAPTIVE) & ~(feature->hordeFlags)),
        !          1554:                                                feature->hordeFlags);
        !          1555:                             if (monst) {
        !          1556:                                 monst->bookkeepingFlags |= MB_JUST_SUMMONED;
        !          1557:                             }
        !          1558:                         }
        !          1559:
        !          1560:                         if (feature->monsterID) {
        !          1561:                             monst = monsterAtLoc(featX, featY);
        !          1562:                             if (monst) {
        !          1563:                                 killCreature(monst, true); // If there's already a monster here, quietly bury the body.
        !          1564:                             }
        !          1565:                             monst = generateMonster(feature->monsterID, true, true);
        !          1566:                             if (monst) {
        !          1567:                                 monst->xLoc = featX;
        !          1568:                                 monst->yLoc = featY;
        !          1569:                                 pmap[monst->xLoc][monst->yLoc].flags |= HAS_MONSTER;
        !          1570:                                 monst->bookkeepingFlags |= MB_JUST_SUMMONED;
        !          1571:                             }
        !          1572:                         }
        !          1573:
        !          1574:                         if (monst) {
        !          1575:                             if (!leader) {
        !          1576:                                 leader = monst;
        !          1577:                             }
        !          1578:
        !          1579:                             // Give our item to the monster leader if appropriate.
        !          1580:                             // Actually just remember that we have to give it to this monster; the actual
        !          1581:                             // hand-off happens after we're sure that the machine will succeed.
        !          1582:                             if (theItem && (feature->flags & MF_MONSTER_TAKE_ITEM)) {
        !          1583:                                 torchBearer = monst;
        !          1584:                                 torch = theItem;
        !          1585:                             }
        !          1586:                         }
        !          1587:
        !          1588:                         for (monst = monsters->nextCreature; monst; monst = nextMonst) {
        !          1589:                             // Have to cache the next monster, as the chain can get disrupted by making a monster dormant below.
        !          1590:                             nextMonst = monst->nextCreature;
        !          1591:                             if (monst->bookkeepingFlags & MB_JUST_SUMMONED) {
        !          1592:
        !          1593:                                 // All monsters spawned by a machine are tribemates.
        !          1594:                                 // Assign leader/follower roles if they are not yet assigned.
        !          1595:                                 if (!(monst->bookkeepingFlags & (MB_LEADER | MB_FOLLOWER))) {
        !          1596:                                     if (leader && leader != monst) {
        !          1597:                                         monst->leader = leader;
        !          1598:                                         monst->bookkeepingFlags &= ~MB_LEADER;
        !          1599:                                         monst->bookkeepingFlags |= MB_FOLLOWER;
        !          1600:                                         leader->bookkeepingFlags |= MB_LEADER;
        !          1601:                                     } else {
        !          1602:                                         leader = monst;
        !          1603:                                     }
        !          1604:                                 }
        !          1605:
        !          1606:                                 monst->bookkeepingFlags &= ~MB_JUST_SUMMONED;
        !          1607:                                 p->spawnedMonsters[monsterCount] = monst;
        !          1608:                                 monsterCount++;
        !          1609:                                 if (feature->flags & MF_MONSTER_SLEEPING) {
        !          1610:                                     monst->creatureState = MONSTER_SLEEPING;
        !          1611:                                 }
        !          1612:                                 if (feature->flags & MF_MONSTER_FLEEING) {
        !          1613:                                     monst->creatureState = MONSTER_FLEEING;
        !          1614:                                     monst->creatureMode = MODE_PERM_FLEEING;
        !          1615:                                 }
        !          1616:                                 if (feature->flags & MF_MONSTERS_DORMANT) {
        !          1617:                                     toggleMonsterDormancy(monst);
        !          1618:                                     if (!(feature->flags & MF_MONSTER_SLEEPING) && monst->creatureState != MONSTER_ALLY) {
        !          1619:                                         monst->creatureState = MONSTER_TRACKING_SCENT;
        !          1620:                                     }
        !          1621:                                 }
        !          1622:                                 monst->machineHome = machineNumber; // Monster remembers the machine that spawned it.
        !          1623:                             }
        !          1624:                         }
        !          1625:                     }
        !          1626:                 }
        !          1627:                 theItem = NULL;
        !          1628:
        !          1629:                 // Finished with this instance!
        !          1630:             }
        !          1631:         } while ((feature->flags & MF_REPEAT_UNTIL_NO_PROGRESS) && instance >= feature->minimumInstanceCount);
        !          1632:
        !          1633:         //DEBUG printf("\nFinished feature %i. Here's the candidates map:", feat);
        !          1634:         //DEBUG logBuffer(candidates);
        !          1635:
        !          1636:         if (instance < feature->minimumInstanceCount && !(feature->flags & MF_REPEAT_UNTIL_NO_PROGRESS)) {
        !          1637:             // failure! abort!
        !          1638:
        !          1639:             if (D_MESSAGE_MACHINE_GENERATION) printf("\nDepth %i: Failed to place blueprint %i because of feature %i; needed %i instances but got only %i.",
        !          1640:                          rogue.depthLevel, bp, feat, feature->minimumInstanceCount, instance);
        !          1641:
        !          1642:             // Restore the map to how it was before we touched it.
        !          1643:             copyMap(p->levelBackup, pmap);
        !          1644:             abortItemsAndMonsters(p->spawnedItems, p->spawnedMonsters);
        !          1645:             freeGrid(distanceMap);
        !          1646:             free(p);
        !          1647:             return false;
        !          1648:         }
        !          1649:     }
        !          1650:
        !          1651:     // Clear out the interior flag for all non-wired cells, if requested.
        !          1652:     if (blueprintCatalog[bp].flags & BP_NO_INTERIOR_FLAG) {
        !          1653:         for(i=0; i<DCOLS; i++) {
        !          1654:             for(j=0; j<DROWS; j++) {
        !          1655:                 if (pmap[i][j].machineNumber == machineNumber
        !          1656:                     && !cellHasTMFlag(i, j, (TM_IS_WIRED | TM_IS_CIRCUIT_BREAKER))) {
        !          1657:
        !          1658:                     pmap[i][j].flags &= ~IS_IN_MACHINE;
        !          1659:                     pmap[i][j].machineNumber = 0;
        !          1660:                 }
        !          1661:             }
        !          1662:         }
        !          1663:     }
        !          1664:
        !          1665:     if (torchBearer && torch) {
        !          1666:         if (torchBearer->carriedItem) {
        !          1667:             deleteItem(torchBearer->carriedItem);
        !          1668:         }
        !          1669:         removeItemFromChain(torch, floorItems);
        !          1670:         torchBearer->carriedItem = torch;
        !          1671:     }
        !          1672:
        !          1673:     freeGrid(distanceMap);
        !          1674:     if (D_MESSAGE_MACHINE_GENERATION) printf("\nDepth %i: Built a machine from blueprint %i with an origin at (%i, %i).", rogue.depthLevel, bp, originX, originY);
        !          1675:
        !          1676:     //Pass created items and monsters to parent where they will be deleted on failure to place parent machine
        !          1677:     if (parentSpawnedItems) {
        !          1678:         for (i=0; i<itemCount; i++) {
        !          1679:             parentSpawnedItems[i] = p->spawnedItems[i];
        !          1680:         }
        !          1681:     }
        !          1682:     if (parentSpawnedMonsters) {
        !          1683:         for (i=0; i<monsterCount; i++) {
        !          1684:             parentSpawnedMonsters[i] = p->spawnedMonsters[i];
        !          1685:         }
        !          1686:     }
        !          1687:
        !          1688:     free(p);
        !          1689:     return true;
        !          1690: }
        !          1691:
        !          1692: // add machines to the dungeon.
        !          1693: void addMachines() {
        !          1694:     short machineCount, failsafe;
        !          1695:     short randomMachineFactor;
        !          1696:
        !          1697:     analyzeMap(true);
        !          1698:
        !          1699:     // Add the amulet holder if it's depth 26:
        !          1700:     if (rogue.depthLevel == AMULET_LEVEL) {
        !          1701:         for (failsafe = 50; failsafe; failsafe--) {
        !          1702:             if (buildAMachine(MT_AMULET_AREA, -1, -1, 0, NULL, NULL, NULL)) {
        !          1703:                 break;
        !          1704:             }
        !          1705:         }
        !          1706:     }
        !          1707:
        !          1708:     // Add reward rooms, if any:
        !          1709:     machineCount = 0;
        !          1710:     while (rogue.depthLevel <= AMULET_LEVEL
        !          1711:         && (rogue.rewardRoomsGenerated + machineCount) * 4 + 2 < rogue.depthLevel * MACHINES_FACTOR / FP_FACTOR) {
        !          1712:         // try to build at least one every four levels on average
        !          1713:         machineCount++;
        !          1714:     }
        !          1715:     randomMachineFactor = (rogue.depthLevel < 3 && (rogue.rewardRoomsGenerated + machineCount) == 0 ? 40 : 15);
        !          1716:     while (rand_percent(max(randomMachineFactor, 15 * MACHINES_FACTOR / FP_FACTOR)) && machineCount < 100) {
        !          1717:         randomMachineFactor = 15;
        !          1718:         machineCount++;
        !          1719:     }
        !          1720:
        !          1721:     for (failsafe = 50; machineCount && failsafe; failsafe--) {
        !          1722:         if (buildAMachine(-1, -1, -1, BP_REWARD, NULL, NULL, NULL)) {
        !          1723:             machineCount--;
        !          1724:             rogue.rewardRoomsGenerated++;
        !          1725:         }
        !          1726:     }
        !          1727: }
        !          1728:
        !          1729: // Add terrain, DFs and flavor machines. Includes traps, torches, funguses, flavor machines, etc.
        !          1730: // If buildAreaMachines is true, build ONLY the autogenerators that include machines.
        !          1731: // If false, build all EXCEPT the autogenerators that include machines.
        !          1732: void runAutogenerators(boolean buildAreaMachines) {
        !          1733:     short AG, count, x, y, i;
        !          1734:     const autoGenerator *gen;
        !          1735:     char grid[DCOLS][DROWS];
        !          1736:
        !          1737:     // Cycle through the autoGenerators.
        !          1738:     for (AG=1; AG<NUMBER_AUTOGENERATORS; AG++) {
        !          1739:
        !          1740:         // Shortcut:
        !          1741:         gen = &(autoGeneratorCatalog[AG]);
        !          1742:
        !          1743:         if (gen->machine > 0 == buildAreaMachines) {
        !          1744:
        !          1745:             // Enforce depth constraints.
        !          1746:             if (rogue.depthLevel < gen->minDepth || rogue.depthLevel > gen->maxDepth) {
        !          1747:                 continue;
        !          1748:             }
        !          1749:
        !          1750:             // Decide how many of this AG to build.
        !          1751:             count = min((gen->minNumberIntercept + rogue.depthLevel * gen->minNumberSlope) / 100, gen->maxNumber);
        !          1752:             while (rand_percent(gen->frequency) && count < gen->maxNumber) {
        !          1753:                 count++;
        !          1754:             }
        !          1755:
        !          1756:             // Build that many instances.
        !          1757:             for (i = 0; i < count; i++) {
        !          1758:
        !          1759:                 // Find a location for DFs and terrain generations.
        !          1760:                 //if (randomMatchingLocation(&x, &y, gen->requiredDungeonFoundationType, NOTHING, -1)) {
        !          1761:                 //if (randomMatchingLocation(&x, &y, -1, -1, gen->requiredDungeonFoundationType)) {
        !          1762:                 if (randomMatchingLocation(&x, &y, gen->requiredDungeonFoundationType, gen->requiredLiquidFoundationType, -1)) {
        !          1763:
        !          1764:                     // Spawn the DF.
        !          1765:                     if (gen->DFType) {
        !          1766:                         spawnDungeonFeature(x, y, &(dungeonFeatureCatalog[gen->DFType]), false, true);
        !          1767:
        !          1768:                         if (D_INSPECT_LEVELGEN) {
        !          1769:                             dumpLevelToScreen();
        !          1770:                             hiliteCell(x, y, &yellow, 50, true);
        !          1771:                             temporaryMessage("Dungeon feature added.", true);
        !          1772:                         }
        !          1773:                     }
        !          1774:
        !          1775:                     // Spawn the terrain if it's got the priority to spawn there and won't disrupt connectivity.
        !          1776:                     if (gen->terrain
        !          1777:                         && tileCatalog[pmap[x][y].layers[gen->layer]].drawPriority >= tileCatalog[gen->terrain].drawPriority) {
        !          1778:
        !          1779:                         // Check connectivity.
        !          1780:                         zeroOutGrid(grid);
        !          1781:                         grid[x][y] = true;
        !          1782:                         if (!(tileCatalog[gen->terrain].flags & T_PATHING_BLOCKER)
        !          1783:                             || !levelIsDisconnectedWithBlockingMap(grid, false)) {
        !          1784:
        !          1785:                             // Build!
        !          1786:                             pmap[x][y].layers[gen->layer] = gen->terrain;
        !          1787:
        !          1788:                             if (D_INSPECT_LEVELGEN) {
        !          1789:                                 dumpLevelToScreen();
        !          1790:                                 hiliteCell(x, y, &yellow, 50, true);
        !          1791:                                 temporaryMessage("Terrain added.", true);
        !          1792:                             }
        !          1793:                         }
        !          1794:                     }
        !          1795:                 }
        !          1796:
        !          1797:                 // Attempt to build the machine if requested.
        !          1798:                 // Machines will find their own locations, so it will not be at the same place as terrain and DF.
        !          1799:                 if (gen->machine > 0) {
        !          1800:                     buildAMachine(gen->machine, -1, -1, 0, NULL, NULL, NULL);
        !          1801:                 }
        !          1802:             }
        !          1803:         }
        !          1804:     }
        !          1805: }
        !          1806:
        !          1807: // Knock down the boundaries between similar lakes where possible.
        !          1808: void cleanUpLakeBoundaries() {
        !          1809:     short i, j, x, y, failsafe, layer;
        !          1810:     boolean reverse, madeChange;
        !          1811:     unsigned long subjectFlags;
        !          1812:
        !          1813:     reverse = true;
        !          1814:
        !          1815:     failsafe = 100;
        !          1816:     do {
        !          1817:         madeChange = false;
        !          1818:         reverse = !reverse;
        !          1819:         failsafe--;
        !          1820:
        !          1821:         for (i = (reverse ? DCOLS - 2 : 1);
        !          1822:              (reverse ? i > 0 : i < DCOLS - 1);
        !          1823:              (reverse ? i-- : i++)) {
        !          1824:
        !          1825:             for (j = (reverse ? DROWS - 2 : 1);
        !          1826:                  (reverse ? j > 0 : j < DROWS - 1);
        !          1827:                  (reverse ? j-- : j++)) {
        !          1828:
        !          1829:                 //assert(i >= 1 && i <= DCOLS - 2 && j >= 1 && j <= DROWS - 2);
        !          1830:
        !          1831:                 //if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)
        !          1832:                 if (cellHasTerrainFlag(i, j, T_LAKE_PATHING_BLOCKER | T_OBSTRUCTS_PASSABILITY)
        !          1833:                     && !cellHasTMFlag(i, j, TM_IS_SECRET)
        !          1834:                     && !(pmap[i][j].flags & IMPREGNABLE)) {
        !          1835:
        !          1836:                     subjectFlags = terrainFlags(i, j) & (T_LAKE_PATHING_BLOCKER | T_OBSTRUCTS_PASSABILITY);
        !          1837:
        !          1838:                     x = y = 0;
        !          1839:                     if ((terrainFlags(i - 1, j) & T_LAKE_PATHING_BLOCKER & ~subjectFlags)
        !          1840:                         && !cellHasTMFlag(i - 1, j, TM_IS_SECRET)
        !          1841:                         && !cellHasTMFlag(i + 1, j, TM_IS_SECRET)
        !          1842:                         && (terrainFlags(i - 1, j) & T_LAKE_PATHING_BLOCKER & ~subjectFlags) == (terrainFlags(i + 1, j) & T_LAKE_PATHING_BLOCKER & ~subjectFlags)) {
        !          1843:                         x = i + 1;
        !          1844:                         y = j;
        !          1845:                     } else if ((terrainFlags(i, j - 1) & T_LAKE_PATHING_BLOCKER & ~subjectFlags)
        !          1846:                                && !cellHasTMFlag(i, j - 1, TM_IS_SECRET)
        !          1847:                                && !cellHasTMFlag(i, j + 1, TM_IS_SECRET)
        !          1848:                                && (terrainFlags(i, j - 1) & T_LAKE_PATHING_BLOCKER & ~subjectFlags) == (terrainFlags(i, j + 1) & T_LAKE_PATHING_BLOCKER & ~subjectFlags)) {
        !          1849:                         x = i;
        !          1850:                         y = j + 1;
        !          1851:                     }
        !          1852:                     if (x) {
        !          1853:                         madeChange = true;
        !          1854:                         for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
        !          1855:                             pmap[i][j].layers[layer] = pmap[x][y].layers[layer];
        !          1856:                         }
        !          1857:                         //pmap[i][j].layers[DUNGEON] = CRYSTAL_WALL;
        !          1858:                     }
        !          1859:                 }
        !          1860:             }
        !          1861:         }
        !          1862:     } while (madeChange && failsafe > 0);
        !          1863: }
        !          1864:
        !          1865: void removeDiagonalOpenings() {
        !          1866:     short i, j, k, x1, y1, x2, layer;
        !          1867:     boolean diagonalCornerRemoved;
        !          1868:
        !          1869:     do {
        !          1870:         diagonalCornerRemoved = false;
        !          1871:         for (i=0; i<DCOLS-1; i++) {
        !          1872:             for (j=0; j<DROWS-1; j++) {
        !          1873:                 for (k=0; k<=1; k++) {
        !          1874:                     if (!(tileCatalog[pmap[i + k][j].layers[DUNGEON]].flags & T_OBSTRUCTS_PASSABILITY)
        !          1875:                         && (tileCatalog[pmap[i + (1-k)][j].layers[DUNGEON]].flags & T_OBSTRUCTS_PASSABILITY)
        !          1876:                         && (tileCatalog[pmap[i + (1-k)][j].layers[DUNGEON]].flags & T_OBSTRUCTS_DIAGONAL_MOVEMENT)
        !          1877:                         && (tileCatalog[pmap[i + k][j+1].layers[DUNGEON]].flags & T_OBSTRUCTS_PASSABILITY)
        !          1878:                         && (tileCatalog[pmap[i + k][j+1].layers[DUNGEON]].flags & T_OBSTRUCTS_DIAGONAL_MOVEMENT)
        !          1879:                         && !(tileCatalog[pmap[i + (1-k)][j+1].layers[DUNGEON]].flags & T_OBSTRUCTS_PASSABILITY)) {
        !          1880:
        !          1881:                         if (rand_percent(50)) {
        !          1882:                             x1 = i + (1-k);
        !          1883:                             x2 = i + k;
        !          1884:                             y1 = j;
        !          1885:                         } else {
        !          1886:                             x1 = i + k;
        !          1887:                             x2 = i + (1-k);
        !          1888:                             y1 = j + 1;
        !          1889:                         }
        !          1890:                         if (!(pmap[x1][y1].flags & HAS_MONSTER) && pmap[x1][y1].machineNumber == 0) {
        !          1891:                             diagonalCornerRemoved = true;
        !          1892:                             for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
        !          1893:                                 pmap[x1][y1].layers[layer] = pmap[x2][y1].layers[layer];
        !          1894:                             }
        !          1895:                         }
        !          1896:                     }
        !          1897:                 }
        !          1898:             }
        !          1899:         }
        !          1900:     } while (diagonalCornerRemoved == true);
        !          1901: }
        !          1902:
        !          1903: void insertRoomAt(short **dungeonMap, short **roomMap, const short roomToDungeonX, const short roomToDungeonY, const short xRoom, const short yRoom) {
        !          1904:     short newX, newY;
        !          1905:     enum directions dir;
        !          1906:
        !          1907:     brogueAssert(coordinatesAreInMap(xRoom + roomToDungeonX, yRoom + roomToDungeonY));
        !          1908:
        !          1909:     dungeonMap[xRoom + roomToDungeonX][yRoom + roomToDungeonY] = 1;
        !          1910:     for (dir = 0; dir < 4; dir++) {
        !          1911:         newX = xRoom + nbDirs[dir][0];
        !          1912:         newY = yRoom + nbDirs[dir][1];
        !          1913:         if (coordinatesAreInMap(newX, newY)
        !          1914:             && roomMap[newX][newY]
        !          1915:             && coordinatesAreInMap(newX + roomToDungeonX, newY + roomToDungeonY)
        !          1916:             && dungeonMap[newX + roomToDungeonX][newY + roomToDungeonY] == 0) {
        !          1917:
        !          1918:             insertRoomAt(dungeonMap, roomMap, roomToDungeonX, roomToDungeonY, newX, newY);
        !          1919:         }
        !          1920:     }
        !          1921: }
        !          1922:
        !          1923: void designCavern(short **grid, short minWidth, short maxWidth, short minHeight, short maxHeight) {
        !          1924:     short destX, destY;
        !          1925:     short caveX, caveY, caveWidth, caveHeight;
        !          1926:     short fillX = 0, fillY = 0;
        !          1927:     boolean foundFillPoint = false;
        !          1928:     short **blobGrid;
        !          1929:     blobGrid = allocGrid();
        !          1930:
        !          1931:     fillGrid(grid, 0);
        !          1932:     createBlobOnGrid(blobGrid,
        !          1933:                      &caveX, &caveY, &caveWidth, &caveHeight,
        !          1934:                      5, minWidth, minHeight, maxWidth, maxHeight, 55, "ffffffttt", "ffffttttt");
        !          1935:
        !          1936: //    colorOverDungeon(&darkGray);
        !          1937: //    hiliteGrid(blobGrid, &tanColor, 80);
        !          1938: //    temporaryMessage("Here's the cave:", true);
        !          1939:
        !          1940:     // Position the new cave in the middle of the grid...
        !          1941:     destX = (DCOLS - caveWidth) / 2;
        !          1942:     destY = (DROWS - caveHeight) / 2;
        !          1943:     // ...pick a floodfill insertion point...
        !          1944:     for (fillX = 0; fillX < DCOLS && !foundFillPoint; fillX++) {
        !          1945:         for (fillY = 0; fillY < DROWS && !foundFillPoint; fillY++) {
        !          1946:             if (blobGrid[fillX][fillY]) {
        !          1947:                 foundFillPoint = true;
        !          1948:             }
        !          1949:         }
        !          1950:     }
        !          1951:     // ...and copy it to the master grid.
        !          1952:     insertRoomAt(grid, blobGrid, destX - caveX, destY - caveY, fillX, fillY);
        !          1953:     freeGrid(blobGrid);
        !          1954: }
        !          1955:
        !          1956: // This is a special room that appears at the entrance to the dungeon on depth 1.
        !          1957: void designEntranceRoom(short **grid) {
        !          1958:     short roomWidth, roomHeight, roomWidth2, roomHeight2, roomX, roomY, roomX2, roomY2;
        !          1959:
        !          1960:     fillGrid(grid, 0);
        !          1961:
        !          1962:     roomWidth = 8;
        !          1963:     roomHeight = 10;
        !          1964:     roomWidth2 = 20;
        !          1965:     roomHeight2 = 5;
        !          1966:     roomX = DCOLS/2 - roomWidth/2 - 1;
        !          1967:     roomY = DROWS - roomHeight - 2;
        !          1968:     roomX2 = DCOLS/2 - roomWidth2/2 - 1;
        !          1969:     roomY2 = DROWS - roomHeight2 - 2;
        !          1970:
        !          1971:     drawRectangleOnGrid(grid, roomX, roomY, roomWidth, roomHeight, 1);
        !          1972:     drawRectangleOnGrid(grid, roomX2, roomY2, roomWidth2, roomHeight2, 1);
        !          1973: }
        !          1974:
        !          1975: void designCrossRoom(short **grid) {
        !          1976:     short roomWidth, roomHeight, roomWidth2, roomHeight2, roomX, roomY, roomX2, roomY2;
        !          1977:
        !          1978:     fillGrid(grid, 0);
        !          1979:
        !          1980:     roomWidth = rand_range(3, 12);
        !          1981:     roomX = rand_range(max(0, DCOLS/2 - (roomWidth - 1)), min(DCOLS, DCOLS/2));
        !          1982:     roomWidth2 = rand_range(4, 20);
        !          1983:     roomX2 = (roomX + (roomWidth / 2) + rand_range(0, 2) + rand_range(0, 2) - 3) - (roomWidth2 / 2);
        !          1984:
        !          1985:     roomHeight = rand_range(3, 7);
        !          1986:     roomY = (DROWS/2 - roomHeight);
        !          1987:
        !          1988:     roomHeight2 = rand_range(2, 5);
        !          1989:     roomY2 = (DROWS/2 - roomHeight2 - (rand_range(0, 2) + rand_range(0, 1)));
        !          1990:
        !          1991:     drawRectangleOnGrid(grid, roomX - 5, roomY + 5, roomWidth, roomHeight, 1);
        !          1992:     drawRectangleOnGrid(grid, roomX2 - 5, roomY2 + 5, roomWidth2, roomHeight2, 1);
        !          1993: }
        !          1994:
        !          1995: void designSymmetricalCrossRoom(short **grid) {
        !          1996:     short majorWidth, majorHeight, minorWidth, minorHeight;
        !          1997:
        !          1998:     fillGrid(grid, 0);
        !          1999:
        !          2000:     majorWidth = rand_range(4, 8);
        !          2001:     majorHeight = rand_range(4, 5);
        !          2002:
        !          2003:     minorWidth = rand_range(3, 4);
        !          2004:     if (majorHeight % 2 == 0) {
        !          2005:         minorWidth -= 1;
        !          2006:     }
        !          2007:     minorHeight = 3;//rand_range(2, 3);
        !          2008:     if (majorWidth % 2 == 0) {
        !          2009:         minorHeight -= 1;
        !          2010:     }
        !          2011:
        !          2012:     drawRectangleOnGrid(grid, (DCOLS - majorWidth)/2, (DROWS - minorHeight)/2, majorWidth, minorHeight, 1);
        !          2013:     drawRectangleOnGrid(grid, (DCOLS - minorWidth)/2, (DROWS - majorHeight)/2, minorWidth, majorHeight, 1);
        !          2014: }
        !          2015:
        !          2016: void designSmallRoom(short **grid) {
        !          2017:     short width, height;
        !          2018:
        !          2019:     fillGrid(grid, 0);
        !          2020:     width = rand_range(3, 6);
        !          2021:     height = rand_range(2, 4);
        !          2022:     drawRectangleOnGrid(grid, (DCOLS - width) / 2, (DROWS - height) / 2, width, height, 1);
        !          2023: }
        !          2024:
        !          2025: void designCircularRoom(short **grid) {
        !          2026:     short radius;
        !          2027:
        !          2028:     if (rand_percent(5)) {
        !          2029:         radius = rand_range(4, 10);
        !          2030:     } else {
        !          2031:         radius = rand_range(2, 4);
        !          2032:     }
        !          2033:
        !          2034:     fillGrid(grid, 0);
        !          2035:     drawCircleOnGrid(grid, DCOLS/2, DROWS/2, radius, 1);
        !          2036:
        !          2037:     if (radius > 6
        !          2038:         && rand_percent(50)) {
        !          2039:         drawCircleOnGrid(grid, DCOLS/2, DROWS/2, rand_range(3, radius - 3), 0);
        !          2040:     }
        !          2041: }
        !          2042:
        !          2043: void designChunkyRoom(short **grid) {
        !          2044:     short i, x, y;
        !          2045:     short minX, maxX, minY, maxY;
        !          2046:     short chunkCount = rand_range(2, 8);
        !          2047:
        !          2048:     fillGrid(grid, 0);
        !          2049:     drawCircleOnGrid(grid, DCOLS/2, DROWS/2, 2, 1);
        !          2050:     minX = DCOLS/2 - 3;
        !          2051:     maxX = DCOLS/2 + 3;
        !          2052:     minY = DROWS/2 - 3;
        !          2053:     maxY = DROWS/2 + 3;
        !          2054:
        !          2055:     for (i=0; i<chunkCount;) {
        !          2056:         x = rand_range(minX, maxX);
        !          2057:         y = rand_range(minY, maxY);
        !          2058:         if (grid[x][y]) {
        !          2059: //            colorOverDungeon(&darkGray);
        !          2060: //            hiliteGrid(grid, &white, 100);
        !          2061:
        !          2062:             drawCircleOnGrid(grid, x, y, 2, 1);
        !          2063:             i++;
        !          2064:             minX = max(1, min(x - 3, minX));
        !          2065:             maxX = min(DCOLS - 2, max(x + 3, maxX));
        !          2066:             minY = max(1, min(y - 3, minY));
        !          2067:             maxY = min(DROWS - 2, max(y + 3, maxY));
        !          2068:
        !          2069: //            hiliteGrid(grid, &green, 50);
        !          2070: //            temporaryMessage("Added a chunk:", true);
        !          2071:         }
        !          2072:     }
        !          2073: }
        !          2074:
        !          2075: // If the indicated tile is a wall on the room stored in grid, and it could be the site of
        !          2076: // a door out of that room, then return the outbound direction that the door faces.
        !          2077: // Otherwise, return NO_DIRECTION.
        !          2078: enum directions directionOfDoorSite(short **grid, short x, short y) {
        !          2079:     enum directions dir, solutionDir;
        !          2080:     short newX, newY, oppX, oppY;
        !          2081:
        !          2082:     if (grid[x][y]) { // Already occupied
        !          2083:         return NO_DIRECTION;
        !          2084:     }
        !          2085:
        !          2086:     solutionDir = NO_DIRECTION;
        !          2087:     for (dir=0; dir<4; dir++) {
        !          2088:         newX = x + nbDirs[dir][0];
        !          2089:         newY = y + nbDirs[dir][1];
        !          2090:         oppX = x - nbDirs[dir][0];
        !          2091:         oppY = y - nbDirs[dir][1];
        !          2092:         if (coordinatesAreInMap(oppX, oppY)
        !          2093:             && coordinatesAreInMap(newX, newY)
        !          2094:             && grid[oppX][oppY] == 1) {
        !          2095:
        !          2096:             // This grid cell would be a valid tile on which to place a door that, facing outward, points dir.
        !          2097:             if (solutionDir != NO_DIRECTION) {
        !          2098:                 // Already claimed by another direction; no doors here!
        !          2099:                 return NO_DIRECTION;
        !          2100:             }
        !          2101:             solutionDir = dir;
        !          2102:         }
        !          2103:     }
        !          2104:     return solutionDir;
        !          2105: }
        !          2106:
        !          2107: void chooseRandomDoorSites(short **roomMap, short doorSites[4][2]) {
        !          2108:     short i, j, k, newX, newY;
        !          2109:     enum directions dir;
        !          2110:     short **grid;
        !          2111:     boolean doorSiteFailed;
        !          2112:
        !          2113:     grid = allocGrid();
        !          2114:     copyGrid(grid, roomMap);
        !          2115:
        !          2116: //    colorOverDungeon(&darkGray);
        !          2117: //    hiliteGrid(grid, &blue, 100);
        !          2118: //    temporaryMessage("Generating this room:", true);
        !          2119: //    const char dirChars[] = "^v<>";
        !          2120:
        !          2121:     for (i=0; i<DCOLS; i++) {
        !          2122:         for (j=0; j<DROWS; j++) {
        !          2123:             if (!grid[i][j]) {
        !          2124:                 dir = directionOfDoorSite(roomMap, i, j);
        !          2125:                 if (dir != NO_DIRECTION) {
        !          2126:                     // Trace a ray 10 spaces outward from the door site to make sure it doesn't intersect the room.
        !          2127:                     // If it does, it's not a valid door site.
        !          2128:                     newX = i + nbDirs[dir][0];
        !          2129:                     newY = j + nbDirs[dir][1];
        !          2130:                     doorSiteFailed = false;
        !          2131:                     for (k=0; k<10 && coordinatesAreInMap(newX, newY) && !doorSiteFailed; k++) {
        !          2132:                         if (grid[newX][newY]) {
        !          2133:                             doorSiteFailed = true;
        !          2134:                         }
        !          2135:                         newX += nbDirs[dir][0];
        !          2136:                         newY += nbDirs[dir][1];
        !          2137:                     }
        !          2138:                     if (!doorSiteFailed) {
        !          2139: //                        plotCharWithColor(dirChars[dir], mapToWindowX(i), mapToWindowY(j), &black, &green);
        !          2140:                         grid[i][j] = dir + 2; // So as not to conflict with 0 or 1, which are used to indicate exterior/interior.
        !          2141:                     }
        !          2142:                 }
        !          2143:             }
        !          2144:         }
        !          2145:     }
        !          2146:
        !          2147: //    temporaryMessage("Door candidates:", true);
        !          2148:
        !          2149:     // Pick four doors, one in each direction, and store them in doorSites[dir].
        !          2150:     for (dir=0; dir<4; dir++) {
        !          2151:         randomLocationInGrid(grid, &(doorSites[dir][0]), &(doorSites[dir][1]), dir + 2);
        !          2152:     }
        !          2153:
        !          2154:     freeGrid(grid);
        !          2155: }
        !          2156:
        !          2157: void attachHallwayTo(short **grid, short doorSites[4][2]) {
        !          2158:     short i, x, y, newX, newY, dirs[4];
        !          2159:     short length;
        !          2160:     enum directions dir, dir2;
        !          2161:     boolean allowObliqueHallwayExit;
        !          2162:
        !          2163:     // Pick a direction.
        !          2164:     fillSequentialList(dirs, 4);
        !          2165:     shuffleList(dirs, 4);
        !          2166:     for (i=0; i<4; i++) {
        !          2167:         dir = dirs[i];
        !          2168:         if (doorSites[dir][0] != -1
        !          2169:             && doorSites[dir][1] != -1
        !          2170:             && coordinatesAreInMap(doorSites[dir][0] + nbDirs[dir][0] * HORIZONTAL_CORRIDOR_MAX_LENGTH,
        !          2171:                                    doorSites[dir][1] + nbDirs[dir][1] * VERTICAL_CORRIDOR_MAX_LENGTH)) {
        !          2172:                 break; // That's our direction!
        !          2173:         }
        !          2174:     }
        !          2175:     if (i==4) {
        !          2176:         return; // No valid direction for hallways.
        !          2177:     }
        !          2178:
        !          2179:     if (dir == UP || dir == DOWN) {
        !          2180:         length = rand_range(VERTICAL_CORRIDOR_MIN_LENGTH, VERTICAL_CORRIDOR_MAX_LENGTH);
        !          2181:     } else {
        !          2182:         length = rand_range(HORIZONTAL_CORRIDOR_MIN_LENGTH, HORIZONTAL_CORRIDOR_MAX_LENGTH);
        !          2183:     }
        !          2184:
        !          2185:     x = doorSites[dir][0];
        !          2186:     y = doorSites[dir][1];
        !          2187:     for (i = 0; i < length; i++) {
        !          2188:         if (coordinatesAreInMap(x, y)) {
        !          2189:             grid[x][y] = true;
        !          2190:         }
        !          2191:         x += nbDirs[dir][0];
        !          2192:         y += nbDirs[dir][1];
        !          2193:     }
        !          2194:     x = clamp(x - nbDirs[dir][0], 0, DCOLS - 1);
        !          2195:     y = clamp(y - nbDirs[dir][1], 0, DROWS - 1); // Now (x, y) points at the last interior cell of the hallway.
        !          2196:     allowObliqueHallwayExit = rand_percent(15);
        !          2197:     for (dir2 = 0; dir2 < 4; dir2++) {
        !          2198:         newX = x + nbDirs[dir2][0];
        !          2199:         newY = y + nbDirs[dir2][1];
        !          2200:
        !          2201:         if ((dir2 != dir && !allowObliqueHallwayExit)
        !          2202:             || !coordinatesAreInMap(newX, newY)
        !          2203:             || grid[newX][newY]) {
        !          2204:
        !          2205:             doorSites[dir2][0] = -1;
        !          2206:             doorSites[dir2][1] = -1;
        !          2207:         } else {
        !          2208:             doorSites[dir2][0] = newX;
        !          2209:             doorSites[dir2][1] = newY;
        !          2210:         }
        !          2211:     }
        !          2212: }
        !          2213:
        !          2214: // Put a random room shape somewhere on the binary grid,
        !          2215: // and optionally record the coordinates of up to four door sites in doorSites.
        !          2216: // If attachHallway is true, then it will bolt a perpendicular hallway onto the room at one of the four standard door sites,
        !          2217: // and then relocate three of the door sites to radiate from the end of the hallway. (The fourth is defunct.)
        !          2218: // RoomTypeFrequencies specifies the probability of each room type, in the following order:
        !          2219: //      0. Cross room
        !          2220: //      1. Small symmetrical cross room
        !          2221: //      2. Small room
        !          2222: //      3. Circular room
        !          2223: //      4. Chunky room
        !          2224: //      5. Cave
        !          2225: //      6. Cavern (the kind that fills a level)
        !          2226: //      7. Entrance room (the big upside-down T room at the start of depth 1)
        !          2227:
        !          2228: void designRandomRoom(short **grid, boolean attachHallway, short doorSites[4][2],
        !          2229:                       const short roomTypeFrequencies[ROOM_TYPE_COUNT]) {
        !          2230:     short randIndex, i, sum;
        !          2231:     enum directions dir;
        !          2232:
        !          2233:     sum = 0;
        !          2234:     for (i=0; i<ROOM_TYPE_COUNT; i++) {
        !          2235:         sum += roomTypeFrequencies[i];
        !          2236:     }
        !          2237:     randIndex = rand_range(0, sum - 1);
        !          2238:     for (i=0; i<ROOM_TYPE_COUNT; i++) {
        !          2239:         if (randIndex < roomTypeFrequencies[i]) {
        !          2240:             break; // "i" is our room type.
        !          2241:         } else {
        !          2242:             randIndex -= roomTypeFrequencies[i];
        !          2243:         }
        !          2244:     }
        !          2245:     switch (i) {
        !          2246:         case 0:
        !          2247:             designCrossRoom(grid);
        !          2248:             break;
        !          2249:         case 1:
        !          2250:             designSymmetricalCrossRoom(grid);
        !          2251:             break;
        !          2252:         case 2:
        !          2253:             designSmallRoom(grid);
        !          2254:             break;
        !          2255:         case 3:
        !          2256:             designCircularRoom(grid);
        !          2257:             break;
        !          2258:         case 4:
        !          2259:             designChunkyRoom(grid);
        !          2260:             break;
        !          2261:         case 5:
        !          2262:             switch (rand_range(0, 2)) {
        !          2263:                 case 0:
        !          2264:                     designCavern(grid, 3, 12, 4, 8); // Compact cave room.
        !          2265:                     break;
        !          2266:                 case 1:
        !          2267:                     designCavern(grid, 3, 12, 15, DROWS-2); // Large north-south cave room.
        !          2268:                     break;
        !          2269:                 case 2:
        !          2270:                     designCavern(grid, 20, DROWS-2, 4, 8); // Large east-west cave room.
        !          2271:                     break;
        !          2272:                 default:
        !          2273:                     break;
        !          2274:             }
        !          2275:             break;
        !          2276:         case 6:
        !          2277:             designCavern(grid, CAVE_MIN_WIDTH, DCOLS - 2, CAVE_MIN_HEIGHT, DROWS - 2);
        !          2278:             break;
        !          2279:         case 7:
        !          2280:             designEntranceRoom(grid);
        !          2281:             break;
        !          2282:         default:
        !          2283:             break;
        !          2284:     }
        !          2285:
        !          2286:     if (doorSites) {
        !          2287:         chooseRandomDoorSites(grid, doorSites);
        !          2288:         if (attachHallway) {
        !          2289:             dir = rand_range(0, 3);
        !          2290:             for (i=0; doorSites[dir][0] == -1 && i < 3; i++) {
        !          2291:                 dir = (dir + 1) % 4; // Each room will have at least 2 valid directions for doors.
        !          2292:             }
        !          2293:             attachHallwayTo(grid, doorSites);
        !          2294:         }
        !          2295:     }
        !          2296: }
        !          2297:
        !          2298: boolean roomFitsAt(short **dungeonMap, short **roomMap, short roomToDungeonX, short roomToDungeonY) {
        !          2299:     short xRoom, yRoom, xDungeon, yDungeon, i, j;
        !          2300:
        !          2301:     for (xRoom = 0; xRoom < DCOLS; xRoom++) {
        !          2302:         for (yRoom = 0; yRoom < DROWS; yRoom++) {
        !          2303:             if (roomMap[xRoom][yRoom]) {
        !          2304:                 xDungeon = xRoom + roomToDungeonX;
        !          2305:                 yDungeon = yRoom + roomToDungeonY;
        !          2306:
        !          2307:                 for (i = xDungeon - 1; i <= xDungeon + 1; i++) {
        !          2308:                     for (j = yDungeon - 1; j <= yDungeon + 1; j++) {
        !          2309:                         if (!coordinatesAreInMap(i, j)
        !          2310:                             || dungeonMap[i][j] > 0) {
        !          2311:                             return false;
        !          2312:                         }
        !          2313:                     }
        !          2314:                 }
        !          2315:             }
        !          2316:         }
        !          2317:     }
        !          2318:     return true;
        !          2319: }
        !          2320:
        !          2321: void attachRooms(short **grid, const dungeonProfile *theDP, short attempts, short maxRoomCount) {
        !          2322:     short roomsBuilt, roomsAttempted;
        !          2323:     short **roomMap;
        !          2324:     short doorSites[4][2];
        !          2325:     short i, x, y, sCoord[DCOLS*DROWS];
        !          2326:     enum directions dir, oppDir;
        !          2327:
        !          2328:     fillSequentialList(sCoord, DCOLS*DROWS);
        !          2329:     shuffleList(sCoord, DCOLS*DROWS);
        !          2330:
        !          2331:     roomMap = allocGrid();
        !          2332:     for (roomsBuilt = roomsAttempted = 0; roomsBuilt < maxRoomCount && roomsAttempted < attempts; roomsAttempted++) {
        !          2333:         // Build a room in hyperspace.
        !          2334:         fillGrid(roomMap, 0);
        !          2335:         designRandomRoom(roomMap, roomsAttempted <= attempts - 5 && rand_percent(theDP->corridorChance),
        !          2336:                          doorSites, theDP->roomFrequencies);
        !          2337:
        !          2338:         if (D_INSPECT_LEVELGEN) {
        !          2339:             colorOverDungeon(&darkGray);
        !          2340:             hiliteGrid(roomMap, &blue, 100);
        !          2341:             if (doorSites[0][0] != -1) plotCharWithColor('^', mapToWindowX(doorSites[0][0]), mapToWindowY(doorSites[0][1]), &black, &green);
        !          2342:             if (doorSites[1][0] != -1) plotCharWithColor('v', mapToWindowX(doorSites[1][0]), mapToWindowY(doorSites[1][1]), &black, &green);
        !          2343:             if (doorSites[2][0] != -1) plotCharWithColor('<', mapToWindowX(doorSites[2][0]), mapToWindowY(doorSites[2][1]), &black, &green);
        !          2344:             if (doorSites[3][0] != -1) plotCharWithColor('>', mapToWindowX(doorSites[3][0]), mapToWindowY(doorSites[3][1]), &black, &green);
        !          2345:             temporaryMessage("Generating this room:", true);
        !          2346:         }
        !          2347:
        !          2348:         // Slide hyperspace across real space, in a random but predetermined order, until the room matches up with a wall.
        !          2349:         for (i = 0; i < DCOLS*DROWS; i++) {
        !          2350:             x = sCoord[i] / DROWS;
        !          2351:             y = sCoord[i] % DROWS;
        !          2352:
        !          2353:             dir = directionOfDoorSite(grid, x, y);
        !          2354:             oppDir = oppositeDirection(dir);
        !          2355:             if (dir != NO_DIRECTION
        !          2356:                 && doorSites[oppDir][0] != -1
        !          2357:                 && roomFitsAt(grid, roomMap, x - doorSites[oppDir][0], y - doorSites[oppDir][1])) {
        !          2358:
        !          2359:                 // Room fits here.
        !          2360:                 if (D_INSPECT_LEVELGEN) {
        !          2361:                     colorOverDungeon(&darkGray);
        !          2362:                     hiliteGrid(grid, &white, 100);
        !          2363:                 }
        !          2364:                 insertRoomAt(grid, roomMap, x - doorSites[oppDir][0], y - doorSites[oppDir][1], doorSites[oppDir][0], doorSites[oppDir][1]);
        !          2365:                 grid[x][y] = 2; // Door site.
        !          2366:                 if (D_INSPECT_LEVELGEN) {
        !          2367:                     hiliteGrid(grid, &green, 50);
        !          2368:                     temporaryMessage("Added room.", true);
        !          2369:                 }
        !          2370:                 roomsBuilt++;
        !          2371:                 break;
        !          2372:             }
        !          2373:         }
        !          2374:     }
        !          2375:
        !          2376:     freeGrid(roomMap);
        !          2377: }
        !          2378:
        !          2379: void adjustDungeonProfileForDepth(dungeonProfile *theProfile) {
        !          2380:     const short descentPercent = clamp(100 * (rogue.depthLevel - 1) / (AMULET_LEVEL - 1), 0, 100);
        !          2381:
        !          2382:     theProfile->roomFrequencies[0] += 20 * (100 - descentPercent) / 100;
        !          2383:     theProfile->roomFrequencies[1] += 10 * (100 - descentPercent) / 100;
        !          2384:     theProfile->roomFrequencies[3] +=  7 * (100 - descentPercent) / 100;
        !          2385:     theProfile->roomFrequencies[5] += 10 * descentPercent / 100;
        !          2386:
        !          2387:     theProfile->corridorChance += 80 * (100 - descentPercent) / 100;
        !          2388: }
        !          2389:
        !          2390: void adjustDungeonFirstRoomProfileForDepth(dungeonProfile *theProfile) {
        !          2391:     short i;
        !          2392:     const short descentPercent = clamp(100 * (rogue.depthLevel - 1) / (AMULET_LEVEL - 1), 0, 100);
        !          2393:
        !          2394:     if (rogue.depthLevel == 1) {
        !          2395:         // All dungeons start with the entrance room on depth 1.
        !          2396:         for (i = 0; i < ROOM_TYPE_COUNT; i++) {
        !          2397:             theProfile->roomFrequencies[i] = 0;
        !          2398:         }
        !          2399:         theProfile->roomFrequencies[7] = 1;
        !          2400:     } else {
        !          2401:         theProfile->roomFrequencies[6] += 50 * descentPercent / 100;
        !          2402:     }
        !          2403: }
        !          2404:
        !          2405: // Called by digDungeon().
        !          2406: // Slaps a bunch of rooms and hallways into the grid.
        !          2407: // On the grid, a 0 denotes granite, a 1 denotes floor, and a 2 denotes a possible door site.
        !          2408: // -1 denotes off-limits areas -- rooms can't be placed there and also can't sprout off of there.
        !          2409: // Parent function will translate this grid into pmap[][] to make floors, walls, doors, etc.
        !          2410: void carveDungeon(short **grid) {
        !          2411:     dungeonProfile theDP, theFirstRoomDP;
        !          2412:
        !          2413:     theDP = dungeonProfileCatalog[DP_BASIC];
        !          2414:     adjustDungeonProfileForDepth(&theDP);
        !          2415:
        !          2416:     theFirstRoomDP = dungeonProfileCatalog[DP_BASIC_FIRST_ROOM];
        !          2417:     adjustDungeonFirstRoomProfileForDepth(&theFirstRoomDP);
        !          2418:
        !          2419:     designRandomRoom(grid, false, NULL, theFirstRoomDP.roomFrequencies);
        !          2420:
        !          2421:     if (D_INSPECT_LEVELGEN) {
        !          2422:         colorOverDungeon(&darkGray);
        !          2423:         hiliteGrid(grid, &white, 100);
        !          2424:         temporaryMessage("First room placed:", true);
        !          2425:     }
        !          2426:
        !          2427:     attachRooms(grid, &theDP, 35, 35);
        !          2428:
        !          2429: //    colorOverDungeon(&darkGray);
        !          2430: //    hiliteGrid(grid, &white, 100);
        !          2431: //    temporaryMessage("How does this finished level look?", true);
        !          2432: }
        !          2433:
        !          2434: void finishWalls(boolean includingDiagonals) {
        !          2435:     short i, j, x1, y1;
        !          2436:     boolean foundExposure;
        !          2437:     enum directions dir;
        !          2438:
        !          2439:     for (i=0; i<DCOLS; i++) {
        !          2440:         for (j=0; j<DROWS; j++) {
        !          2441:             if (pmap[i][j].layers[DUNGEON] == GRANITE) {
        !          2442:                 foundExposure = false;
        !          2443:                 for (dir = 0; dir < (includingDiagonals ? 8 : 4) && !foundExposure; dir++) {
        !          2444:                     x1 = i + nbDirs[dir][0];
        !          2445:                     y1 = j + nbDirs[dir][1];
        !          2446:                     if (coordinatesAreInMap(x1, y1)
        !          2447:                         && (!cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_VISION) || !cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_PASSABILITY))) {
        !          2448:
        !          2449:                         pmap[i][j].layers[DUNGEON] = WALL;
        !          2450:                         foundExposure = true;
        !          2451:                     }
        !          2452:                 }
        !          2453:             } else if (pmap[i][j].layers[DUNGEON] == WALL) {
        !          2454:                 foundExposure = false;
        !          2455:                 for (dir = 0; dir < (includingDiagonals ? 8 : 4) && !foundExposure; dir++) {
        !          2456:                     x1 = i + nbDirs[dir][0];
        !          2457:                     y1 = j + nbDirs[dir][1];
        !          2458:                     if (coordinatesAreInMap(x1, y1)
        !          2459:                         && (!cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_VISION) || !cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_PASSABILITY))) {
        !          2460:
        !          2461:                         foundExposure = true;
        !          2462:                     }
        !          2463:                 }
        !          2464:                 if (foundExposure == false) {
        !          2465:                     pmap[i][j].layers[DUNGEON] = GRANITE;
        !          2466:                 }
        !          2467:             }
        !          2468:         }
        !          2469:     }
        !          2470: }
        !          2471:
        !          2472: void liquidType(short *deep, short *shallow, short *shallowWidth) {
        !          2473:     short randMin, randMax, rand;
        !          2474:
        !          2475:     randMin = (rogue.depthLevel < 4 ? 1 : 0); // no lava before level 4
        !          2476:     randMax = (rogue.depthLevel < 17 ? 2 : 3); // no brimstone before level 18
        !          2477:     rand = rand_range(randMin, randMax);
        !          2478:     if (rogue.depthLevel == DEEPEST_LEVEL) {
        !          2479:         rand = 1;
        !          2480:     }
        !          2481:
        !          2482:     switch(rand) {
        !          2483:         case 0:
        !          2484:             *deep = LAVA;
        !          2485:             *shallow = NOTHING;
        !          2486:             *shallowWidth = 0;
        !          2487:             break;
        !          2488:         case 1:
        !          2489:             *deep = DEEP_WATER;
        !          2490:             *shallow = SHALLOW_WATER;
        !          2491:             *shallowWidth = 2;
        !          2492:             break;
        !          2493:         case 2:
        !          2494:             *deep = CHASM;
        !          2495:             *shallow = CHASM_EDGE;
        !          2496:             *shallowWidth = 1;
        !          2497:             break;
        !          2498:         case 3:
        !          2499:             *deep = INERT_BRIMSTONE;
        !          2500:             *shallow = OBSIDIAN;
        !          2501:             *shallowWidth = 2;
        !          2502:             break;
        !          2503:     }
        !          2504: }
        !          2505:
        !          2506: // Fills a lake marked in unfilledLakeMap with the specified liquid type, scanning outward to reach other lakes within scanWidth.
        !          2507: // Any wreath of shallow liquid must be done elsewhere.
        !          2508: void fillLake(short x, short y, short liquid, short scanWidth, char wreathMap[DCOLS][DROWS], short **unfilledLakeMap) {
        !          2509:     short i, j;
        !          2510:
        !          2511:     for (i = x - scanWidth; i <= x + scanWidth; i++) {
        !          2512:         for (j = y - scanWidth; j <= y + scanWidth; j++) {
        !          2513:             if (coordinatesAreInMap(i, j) && unfilledLakeMap[i][j]) {
        !          2514:                 unfilledLakeMap[i][j] = false;
        !          2515:                 pmap[i][j].layers[LIQUID] = liquid;
        !          2516:                 wreathMap[i][j] = 1;
        !          2517:                 fillLake(i, j, liquid, scanWidth, wreathMap, unfilledLakeMap);  // recursive
        !          2518:             }
        !          2519:         }
        !          2520:     }
        !          2521: }
        !          2522:
        !          2523: void lakeFloodFill(short x, short y, short **floodMap, short **grid, short **lakeMap, short dungeonToGridX, short dungeonToGridY) {
        !          2524:     short newX, newY;
        !          2525:     enum directions dir;
        !          2526:
        !          2527:     floodMap[x][y] = true;
        !          2528:     for (dir=0; dir<4; dir++) {
        !          2529:         newX = x + nbDirs[dir][0];
        !          2530:         newY = y + nbDirs[dir][1];
        !          2531:         if (coordinatesAreInMap(newX, newY)
        !          2532:             && !floodMap[newX][newY]
        !          2533:             && (!cellHasTerrainFlag(newX, newY, T_PATHING_BLOCKER) || cellHasTMFlag(newX, newY, TM_CONNECTS_LEVEL))
        !          2534:             && !lakeMap[newX][newY]
        !          2535:             && (!coordinatesAreInMap(newX+dungeonToGridX, newY+dungeonToGridY) || !grid[newX+dungeonToGridX][newY+dungeonToGridY])) {
        !          2536:
        !          2537:             lakeFloodFill(newX, newY, floodMap, grid, lakeMap, dungeonToGridX, dungeonToGridY);
        !          2538:         }
        !          2539:     }
        !          2540: }
        !          2541:
        !          2542: boolean lakeDisruptsPassability(short **grid, short **lakeMap, short dungeonToGridX, short dungeonToGridY) {
        !          2543:     boolean result;
        !          2544:     short i, j, x, y;
        !          2545:     short **floodMap;
        !          2546:
        !          2547:     floodMap = allocGrid();
        !          2548:     fillGrid(floodMap, 0);
        !          2549:     x = y = -1;
        !          2550:     // Get starting location for the fill.
        !          2551:     for (i=0; i<DCOLS && x == -1; i++) {
        !          2552:         for (j=0; j<DROWS && x == -1; j++) {
        !          2553:             if (!cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)
        !          2554:                 && !lakeMap[i][j]
        !          2555:                 && (!coordinatesAreInMap(i+dungeonToGridX, j+dungeonToGridY) || !grid[i+dungeonToGridX][j+dungeonToGridY])) {
        !          2556:
        !          2557:                 x = i;
        !          2558:                 y = j;
        !          2559:             }
        !          2560:         }
        !          2561:     }
        !          2562:     brogueAssert(x != -1);
        !          2563:     // Do the flood fill.
        !          2564:     lakeFloodFill(x, y, floodMap, grid, lakeMap, dungeonToGridX, dungeonToGridY);
        !          2565:
        !          2566:     // See if any dry tiles weren't reached by the flood fill.
        !          2567:     result = false;
        !          2568:     for (i=0; i<DCOLS && result == false; i++) {
        !          2569:         for (j=0; j<DROWS && result == false; j++) {
        !          2570:             if (!cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)
        !          2571:                 && !lakeMap[i][j]
        !          2572:                 && !floodMap[i][j]
        !          2573:                 && (!coordinatesAreInMap(i+dungeonToGridX, j+dungeonToGridY) || !grid[i+dungeonToGridX][j+dungeonToGridY])) {
        !          2574:
        !          2575: //                if (D_INSPECT_LEVELGEN) {
        !          2576: //                    dumpLevelToScreen();
        !          2577: //                    hiliteGrid(lakeMap, &darkBlue, 75);
        !          2578: //                    hiliteGrid(floodMap, &white, 20);
        !          2579: //                    plotCharWithColor('X', mapToWindowX(i), mapToWindowY(j), &black, &red);
        !          2580: //                    temporaryMessage("Failed here.", true);
        !          2581: //                }
        !          2582:
        !          2583:                 result = true;
        !          2584:             }
        !          2585:         }
        !          2586:     }
        !          2587:
        !          2588:     freeGrid(floodMap);
        !          2589:     return result;
        !          2590: }
        !          2591:
        !          2592: void designLakes(short **lakeMap) {
        !          2593:     short i, j, k;
        !          2594:     short x, y;
        !          2595:     short lakeMaxHeight, lakeMaxWidth;
        !          2596:     short lakeX, lakeY, lakeWidth, lakeHeight;
        !          2597:
        !          2598:     short **grid; // Holds the current lake.
        !          2599:
        !          2600:     grid = allocGrid();
        !          2601:     fillGrid(lakeMap, 0);
        !          2602:     for (lakeMaxHeight = 15, lakeMaxWidth = 30; lakeMaxHeight >=10; lakeMaxHeight--, lakeMaxWidth -= 2) { // lake generations
        !          2603:
        !          2604:         fillGrid(grid, 0);
        !          2605:         createBlobOnGrid(grid, &lakeX, &lakeY, &lakeWidth, &lakeHeight, 5, 4, 4, lakeMaxWidth, lakeMaxHeight, 55, "ffffftttt", "ffffttttt");
        !          2606:
        !          2607: //        if (D_INSPECT_LEVELGEN) {
        !          2608: //            colorOverDungeon(&darkGray);
        !          2609: //            hiliteGrid(grid, &white, 100);
        !          2610: //            temporaryMessage("Generated a lake.", true);
        !          2611: //        }
        !          2612:
        !          2613:         for (k=0; k<20; k++) { // placement attempts
        !          2614:             // propose a position for the top-left of the grid in the dungeon
        !          2615:             x = rand_range(1 - lakeX, DCOLS - lakeWidth - lakeX - 2);
        !          2616:             y = rand_range(1 - lakeY, DROWS - lakeHeight - lakeY - 2);
        !          2617:
        !          2618:             if (!lakeDisruptsPassability(grid, lakeMap, -x, -y)) { // level with lake is completely connected
        !          2619:                 //printf("Placed a lake!");
        !          2620:
        !          2621:                 // copy in lake
        !          2622:                 for (i = 0; i < lakeWidth; i++) {
        !          2623:                     for (j = 0; j < lakeHeight; j++) {
        !          2624:                         if (grid[i + lakeX][j + lakeY]) {
        !          2625:                             lakeMap[i + lakeX + x][j + lakeY + y] = true;
        !          2626:                             pmap[i + lakeX + x][j + lakeY + y].layers[DUNGEON] = FLOOR;
        !          2627:                         }
        !          2628:                     }
        !          2629:                 }
        !          2630:
        !          2631:                 if (D_INSPECT_LEVELGEN) {
        !          2632:                     dumpLevelToScreen();
        !          2633:                     hiliteGrid(lakeMap, &white, 50);
        !          2634:                     temporaryMessage("Added a lake location.", true);
        !          2635:                 }
        !          2636:                 break;
        !          2637:             }
        !          2638:         }
        !          2639:     }
        !          2640:     freeGrid(grid);
        !          2641: }
        !          2642:
        !          2643: void createWreath(short shallowLiquid, short wreathWidth, char wreathMap[DCOLS][DROWS]) {
        !          2644:     short i, j, k, l;
        !          2645:     for (i=0; i<DCOLS; i++) {
        !          2646:         for (j=0; j<DROWS; j++) {
        !          2647:             if (wreathMap[i][j]) {
        !          2648:                 for (k = i-wreathWidth; k<= i+wreathWidth; k++) {
        !          2649:                     for (l = j-wreathWidth; l <= j+wreathWidth; l++) {
        !          2650:                         if (coordinatesAreInMap(k, l) && pmap[k][l].layers[LIQUID] == NOTHING
        !          2651:                             && (i-k)*(i-k) + (j-l)*(j-l) <= wreathWidth*wreathWidth) {
        !          2652:                             pmap[k][l].layers[LIQUID] = shallowLiquid;
        !          2653:                             if (pmap[k][l].layers[DUNGEON] == DOOR) {
        !          2654:                                 pmap[k][l].layers[DUNGEON] = FLOOR;
        !          2655:                             }
        !          2656:                         }
        !          2657:                     }
        !          2658:                 }
        !          2659:             }
        !          2660:         }
        !          2661:     }
        !          2662: }
        !          2663:
        !          2664: void fillLakes(short **lakeMap) {
        !          2665:     short deepLiquid = CRYSTAL_WALL, shallowLiquid = CRYSTAL_WALL, shallowLiquidWidth = 0;
        !          2666:     char wreathMap[DCOLS][DROWS];
        !          2667:     short i, j;
        !          2668:
        !          2669:     for (i=0; i<DCOLS; i++) {
        !          2670:         for (j=0; j<DROWS; j++) {
        !          2671:             if (lakeMap[i][j]) {
        !          2672:                 liquidType(&deepLiquid, &shallowLiquid, &shallowLiquidWidth);
        !          2673:                 zeroOutGrid(wreathMap);
        !          2674:                 fillLake(i, j, deepLiquid, 4, wreathMap, lakeMap);
        !          2675:                 createWreath(shallowLiquid, shallowLiquidWidth, wreathMap);
        !          2676:
        !          2677:                 if (D_INSPECT_LEVELGEN) {
        !          2678:                     dumpLevelToScreen();
        !          2679:                     hiliteGrid(lakeMap, &white, 75);
        !          2680:                     temporaryMessage("Lake filled.", true);
        !          2681:                 }
        !          2682:             }
        !          2683:         }
        !          2684:     }
        !          2685: }
        !          2686:
        !          2687: void finishDoors() {
        !          2688:     short i, j;
        !          2689:     const short secretDoorChance = clamp((rogue.depthLevel - 1) * 67 / 25, 0, 67);
        !          2690:     for (i=1; i<DCOLS-1; i++) {
        !          2691:         for (j=1; j<DROWS-1; j++) {
        !          2692:             if (pmap[i][j].layers[DUNGEON] == DOOR
        !          2693:                 && pmap[i][j].machineNumber == 0) {
        !          2694:                 if ((!cellHasTerrainFlag(i+1, j, T_OBSTRUCTS_PASSABILITY) || !cellHasTerrainFlag(i-1, j, T_OBSTRUCTS_PASSABILITY))
        !          2695:                     && (!cellHasTerrainFlag(i, j+1, T_OBSTRUCTS_PASSABILITY) || !cellHasTerrainFlag(i, j-1, T_OBSTRUCTS_PASSABILITY))) {
        !          2696:                     // If there's passable terrain to the left or right, and there's passable terrain
        !          2697:                     // above or below, then the door is orphaned and must be removed.
        !          2698:                     pmap[i][j].layers[DUNGEON] = FLOOR;
        !          2699:                 } else if ((cellHasTerrainFlag(i+1, j, T_PATHING_BLOCKER) ? 1 : 0)
        !          2700:                            + (cellHasTerrainFlag(i-1, j, T_PATHING_BLOCKER) ? 1 : 0)
        !          2701:                            + (cellHasTerrainFlag(i, j+1, T_PATHING_BLOCKER) ? 1 : 0)
        !          2702:                            + (cellHasTerrainFlag(i, j-1, T_PATHING_BLOCKER) ? 1 : 0) >= 3) {
        !          2703:                     // If the door has three or more pathing blocker neighbors in the four cardinal directions,
        !          2704:                     // then the door is orphaned and must be removed.
        !          2705:                     pmap[i][j].layers[DUNGEON] = FLOOR;
        !          2706:                 } else if (rand_percent(secretDoorChance)) {
        !          2707:                     pmap[i][j].layers[DUNGEON] = SECRET_DOOR;
        !          2708:                 }
        !          2709:             }
        !          2710:         }
        !          2711:     }
        !          2712: }
        !          2713:
        !          2714: void clearLevel() {
        !          2715:     short i, j;
        !          2716:
        !          2717:     for( i=0; i<DCOLS; i++ ) {
        !          2718:         for( j=0; j<DROWS; j++ ) {
        !          2719:             pmap[i][j].layers[DUNGEON] = GRANITE;
        !          2720:             pmap[i][j].layers[LIQUID] = NOTHING;
        !          2721:             pmap[i][j].layers[GAS] = NOTHING;
        !          2722:             pmap[i][j].layers[SURFACE] = NOTHING;
        !          2723:             pmap[i][j].machineNumber = 0;
        !          2724:             pmap[i][j].rememberedTerrain = NOTHING;
        !          2725:             pmap[i][j].rememberedTerrainFlags = (T_OBSTRUCTS_EVERYTHING);
        !          2726:             pmap[i][j].rememberedTMFlags = 0;
        !          2727:             pmap[i][j].rememberedCellFlags = 0;
        !          2728:             pmap[i][j].rememberedItemCategory = 0;
        !          2729:             pmap[i][j].rememberedItemKind = 0;
        !          2730:             pmap[i][j].rememberedItemQuantity = 0;
        !          2731:             pmap[i][j].rememberedItemOriginDepth = 0;
        !          2732:             pmap[i][j].flags = 0;
        !          2733:             pmap[i][j].volume = 0;
        !          2734:         }
        !          2735:     }
        !          2736: }
        !          2737:
        !          2738: // Scans the map in random order looking for a good place to build a bridge.
        !          2739: // If it finds one, it builds a bridge there, halts and returns true.
        !          2740: boolean buildABridge() {
        !          2741:     short i, j, k, l, i2, j2, nCols[DCOLS], nRows[DROWS];
        !          2742:     short bridgeRatioX, bridgeRatioY;
        !          2743:     boolean foundExposure;
        !          2744:
        !          2745:     bridgeRatioX = (short) (100 + (100 + 100 * rogue.depthLevel / 9) * rand_range(10, 20) / 10);
        !          2746:     bridgeRatioY = (short) (100 + (400 + 100 * rogue.depthLevel / 18) * rand_range(10, 20) / 10);
        !          2747:
        !          2748:     fillSequentialList(nCols, DCOLS);
        !          2749:     shuffleList(nCols, DCOLS);
        !          2750:     fillSequentialList(nRows, DROWS);
        !          2751:     shuffleList(nRows, DROWS);
        !          2752:
        !          2753:     for (i2=1; i2<DCOLS-1; i2++) {
        !          2754:         i = nCols[i2];
        !          2755:         for (j2=1; j2<DROWS-1; j2++) {
        !          2756:             j = nRows[j2];
        !          2757:             if (!cellHasTerrainFlag(i, j, (T_CAN_BE_BRIDGED | T_PATHING_BLOCKER))
        !          2758:                 && !pmap[i][j].machineNumber) {
        !          2759:
        !          2760:                 // try a horizontal bridge
        !          2761:                 foundExposure = false;
        !          2762:                 for (k = i + 1;
        !          2763:                      k < DCOLS // Iterate across the prospective length of the bridge.
        !          2764:                      && !pmap[k][j].machineNumber // No bridges in machines.
        !          2765:                      && cellHasTerrainFlag(k, j, T_CAN_BE_BRIDGED)  // Candidate tile must be chasm.
        !          2766:                      && !cellHasTMFlag(k, j, TM_IS_SECRET) // Can't bridge over secret trapdoors.
        !          2767:                      && !cellHasTerrainFlag(k, j, T_OBSTRUCTS_PASSABILITY)  // Candidate tile cannot be a wall.
        !          2768:                      && cellHasTerrainFlag(k, j-1, (T_CAN_BE_BRIDGED | T_OBSTRUCTS_PASSABILITY))    // Only chasms or walls are permitted next to the length of the bridge.
        !          2769:                      && cellHasTerrainFlag(k, j+1, (T_CAN_BE_BRIDGED | T_OBSTRUCTS_PASSABILITY));
        !          2770:                      k++) {
        !          2771:
        !          2772:                     if (!cellHasTerrainFlag(k, j-1, T_OBSTRUCTS_PASSABILITY) // Can't run against a wall the whole way.
        !          2773:                         && !cellHasTerrainFlag(k, j+1, T_OBSTRUCTS_PASSABILITY)) {
        !          2774:                         foundExposure = true;
        !          2775:                     }
        !          2776:                 }
        !          2777:                 if (k < DCOLS
        !          2778:                     && (k - i > 3) // Can't have bridges shorter than 3 spaces.
        !          2779:                     && foundExposure
        !          2780:                     && !cellHasTerrainFlag(k, j, T_PATHING_BLOCKER | T_CAN_BE_BRIDGED) // Must end on an unobstructed land tile.
        !          2781:                     && !pmap[k][j].machineNumber // Cannot end in a machine.
        !          2782:                     && 100 * pathingDistance(i, j, k, j, T_PATHING_BLOCKER) / (k - i) > bridgeRatioX) { // Must shorten the pathing distance enough.
        !          2783:
        !          2784:                     for (l=i+1; l < k; l++) {
        !          2785:                         pmap[l][j].layers[LIQUID] = BRIDGE;
        !          2786:                     }
        !          2787:                     pmap[i][j].layers[SURFACE] = BRIDGE_EDGE;
        !          2788:                     pmap[k][j].layers[SURFACE] = BRIDGE_EDGE;
        !          2789:                     return true;
        !          2790:                 }
        !          2791:
        !          2792:                 // try a vertical bridge
        !          2793:                 foundExposure = false;
        !          2794:                 for (k = j + 1;
        !          2795:                      k < DROWS
        !          2796:                      && !pmap[i][k].machineNumber
        !          2797:                      && cellHasTerrainFlag(i, k, T_CAN_BE_BRIDGED)
        !          2798:                      && !cellHasTMFlag(i, k, TM_IS_SECRET)
        !          2799:                      && !cellHasTerrainFlag(i, k, T_OBSTRUCTS_PASSABILITY)
        !          2800:                      && cellHasTerrainFlag(i-1, k, (T_CAN_BE_BRIDGED | T_OBSTRUCTS_PASSABILITY))
        !          2801:                      && cellHasTerrainFlag(i+1, k, (T_CAN_BE_BRIDGED | T_OBSTRUCTS_PASSABILITY));
        !          2802:                      k++) {
        !          2803:
        !          2804:                     if (!cellHasTerrainFlag(i-1, k, T_OBSTRUCTS_PASSABILITY)
        !          2805:                         && !cellHasTerrainFlag(i+1, k, T_OBSTRUCTS_PASSABILITY)) {
        !          2806:                         foundExposure = true;
        !          2807:                     }
        !          2808:                 }
        !          2809:                 if (k < DROWS
        !          2810:                     && (k - j > 3)
        !          2811:                     && foundExposure
        !          2812:                     && !cellHasTerrainFlag(i, k, T_PATHING_BLOCKER | T_CAN_BE_BRIDGED)
        !          2813:                     && !pmap[i][k].machineNumber // Cannot end in a machine.
        !          2814:                     && 100 * pathingDistance(i, j, i, k, T_PATHING_BLOCKER) / (k - j) > bridgeRatioY) {
        !          2815:
        !          2816:                     for (l=j+1; l < k; l++) {
        !          2817:                         pmap[i][l].layers[LIQUID] = BRIDGE;
        !          2818:                     }
        !          2819:                     pmap[i][j].layers[SURFACE] = BRIDGE_EDGE;
        !          2820:                     pmap[i][k].layers[SURFACE] = BRIDGE_EDGE;
        !          2821:                     return true;
        !          2822:                 }
        !          2823:             }
        !          2824:         }
        !          2825:     }
        !          2826:     return false;
        !          2827: }
        !          2828:
        !          2829: // This is the master function for digging out a dungeon level.
        !          2830: // Finishing touches -- items, monsters, staircases, etc. -- are handled elsewhere.
        !          2831: void digDungeon() {
        !          2832:     short i, j;
        !          2833:
        !          2834:     short **grid;
        !          2835:
        !          2836:     rogue.machineNumber = 0;
        !          2837:
        !          2838:     topBlobMinX = topBlobMinY = blobWidth = blobHeight = 0;
        !          2839:
        !          2840: #ifdef AUDIT_RNG
        !          2841:     char RNGMessage[100];
        !          2842:     sprintf(RNGMessage, "\n\n\nDigging dungeon level %i:\n", rogue.depthLevel);
        !          2843:     RNGLog(RNGMessage);
        !          2844: #endif
        !          2845:
        !          2846:     // Clear level and fill with granite
        !          2847:     clearLevel();
        !          2848:
        !          2849:     grid = allocGrid();
        !          2850:     carveDungeon(grid);
        !          2851:     addLoops(grid, 20);
        !          2852:     for (i=0; i<DCOLS; i++) {
        !          2853:         for (j=0; j<DROWS; j++) {
        !          2854:             if (grid[i][j] == 1) {
        !          2855:                 pmap[i][j].layers[DUNGEON] = FLOOR;
        !          2856:             } else if (grid[i][j] == 2) {
        !          2857:                 pmap[i][j].layers[DUNGEON] = (rand_percent(60) && rogue.depthLevel < DEEPEST_LEVEL ? DOOR : FLOOR);
        !          2858:             }
        !          2859:         }
        !          2860:     }
        !          2861:     freeGrid(grid);
        !          2862:
        !          2863:     finishWalls(false);
        !          2864:
        !          2865:     if (D_INSPECT_LEVELGEN) {
        !          2866:         dumpLevelToScreen();
        !          2867:         temporaryMessage("Carved into the granite:", true);
        !          2868:     }
        !          2869:     //DEBUG printf("\n%i loops created.", numLoops);
        !          2870:     //DEBUG logLevel();
        !          2871:
        !          2872:     // Time to add lakes and chasms. Strategy is to generate a series of blob lakes of decreasing size. For each lake,
        !          2873:     // propose a position, and then check via a flood fill that the level would remain connected with that placement (i.e. that
        !          2874:     // each passable tile can still be reached). If not, make 9 more placement attempts before abandoning that lake
        !          2875:     // and proceeding to generate the next smaller one.
        !          2876:     // Canvas sizes start at 30x15 and decrease by 2x1 at a time down to a minimum of 20x10. Min generated size is always 4x4.
        !          2877:
        !          2878:     // DEBUG logLevel();
        !          2879:
        !          2880:     // Now design the lakes and then fill them with various liquids (lava, water, chasm, brimstone).
        !          2881:     short **lakeMap = allocGrid();
        !          2882:     designLakes(lakeMap);
        !          2883:     fillLakes(lakeMap);
        !          2884:     freeGrid(lakeMap);
        !          2885:
        !          2886:     // Run the non-machine autoGenerators.
        !          2887:     runAutogenerators(false);
        !          2888:
        !          2889:     // Remove diagonal openings.
        !          2890:     removeDiagonalOpenings();
        !          2891:
        !          2892:     if (D_INSPECT_LEVELGEN) {
        !          2893:         dumpLevelToScreen();
        !          2894:         temporaryMessage("Diagonal openings removed.", true);
        !          2895:     }
        !          2896:
        !          2897:     // Now add some treasure machines.
        !          2898:     addMachines();
        !          2899:
        !          2900:     if (D_INSPECT_LEVELGEN) {
        !          2901:         dumpLevelToScreen();
        !          2902:         temporaryMessage("Machines added.", true);
        !          2903:     }
        !          2904:
        !          2905:     // Run the machine autoGenerators.
        !          2906:     runAutogenerators(true);
        !          2907:
        !          2908:     // Now knock down the boundaries between similar lakes where possible.
        !          2909:     cleanUpLakeBoundaries();
        !          2910:
        !          2911:     if (D_INSPECT_LEVELGEN) {
        !          2912:         dumpLevelToScreen();
        !          2913:         temporaryMessage("Lake boundaries cleaned up.", true);
        !          2914:     }
        !          2915:
        !          2916:     // Now add some bridges.
        !          2917:     while (buildABridge());
        !          2918:
        !          2919:     if (D_INSPECT_LEVELGEN) {
        !          2920:         dumpLevelToScreen();
        !          2921:         temporaryMessage("Bridges added.", true);
        !          2922:     }
        !          2923:
        !          2924:     // Now remove orphaned doors and upgrade some doors to secret doors
        !          2925:     finishDoors();
        !          2926:
        !          2927:     // Now finish any exposed granite with walls and revert any unexposed walls to granite
        !          2928:     finishWalls(true);
        !          2929:
        !          2930:     if (D_INSPECT_LEVELGEN) {
        !          2931:         dumpLevelToScreen();
        !          2932:         temporaryMessage("Finishing touches added. Level has been generated.", true);
        !          2933:     }
        !          2934: }
        !          2935:
        !          2936: void updateMapToShore() {
        !          2937:     short i, j;
        !          2938:     short **costMap;
        !          2939:
        !          2940:     rogue.updatedMapToShoreThisTurn = true;
        !          2941:
        !          2942:     costMap = allocGrid();
        !          2943:
        !          2944:     // Calculate the map to shore for this level
        !          2945:     if (!rogue.mapToShore) {
        !          2946:         rogue.mapToShore = allocGrid();
        !          2947:         fillGrid(rogue.mapToShore, 0);
        !          2948:     }
        !          2949:     for (i=0; i<DCOLS; i++) {
        !          2950:         for (j=0; j<DROWS; j++) {
        !          2951:             if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)) {
        !          2952:                 costMap[i][j] = cellHasTerrainFlag(i, j, T_OBSTRUCTS_DIAGONAL_MOVEMENT) ? PDS_OBSTRUCTION : PDS_FORBIDDEN;
        !          2953:                 rogue.mapToShore[i][j] = 30000;
        !          2954:             } else {
        !          2955:                 costMap[i][j] = 1;
        !          2956:                 rogue.mapToShore[i][j] = (cellHasTerrainFlag(i, j, T_LAVA_INSTA_DEATH | T_IS_DEEP_WATER | T_AUTO_DESCENT)
        !          2957:                                           && !cellHasTMFlag(i, j, TM_IS_SECRET)) ? 30000 : 0;
        !          2958:             }
        !          2959:         }
        !          2960:     }
        !          2961:     dijkstraScan(rogue.mapToShore, costMap, true);
        !          2962:     freeGrid(costMap);
        !          2963: }
        !          2964:
        !          2965: // Calculates the distance map for the given waypoint.
        !          2966: // This is called on all waypoints during setUpWaypoints(),
        !          2967: // and then one waypoint is recalculated per turn thereafter.
        !          2968: void refreshWaypoint(short wpIndex) {
        !          2969:     short **costMap;
        !          2970:     creature *monst;
        !          2971:
        !          2972:     costMap = allocGrid();
        !          2973:     populateGenericCostMap(costMap);
        !          2974:     for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
        !          2975:         if ((monst->creatureState == MONSTER_SLEEPING || (monst->info.flags & MONST_IMMOBILE) || (monst->bookkeepingFlags & MB_CAPTIVE))
        !          2976:             && costMap[monst->xLoc][monst->yLoc] >= 0) {
        !          2977:
        !          2978:             costMap[monst->xLoc][monst->yLoc] = PDS_FORBIDDEN;
        !          2979:         }
        !          2980:     }
        !          2981:     fillGrid(rogue.wpDistance[wpIndex], 30000);
        !          2982:     rogue.wpDistance[wpIndex][rogue.wpCoordinates[wpIndex][0]][rogue.wpCoordinates[wpIndex][1]] = 0;
        !          2983:     dijkstraScan(rogue.wpDistance[wpIndex], costMap, true);
        !          2984:     freeGrid(costMap);
        !          2985: }
        !          2986:
        !          2987: void setUpWaypoints() {
        !          2988:     short i, j, sCoord[DCOLS * DROWS], x, y;
        !          2989:     char grid[DCOLS][DROWS];
        !          2990:
        !          2991:     zeroOutGrid(grid);
        !          2992:     for (i=0; i<DCOLS; i++) {
        !          2993:         for (j=0; j<DROWS; j++) {
        !          2994:             if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_SCENT)) {
        !          2995:                 grid[i][j] = 1;
        !          2996:             }
        !          2997:         }
        !          2998:     }
        !          2999:     rogue.wpCount = 0;
        !          3000:     rogue.wpRefreshTicker = 0;
        !          3001:     fillSequentialList(sCoord, DCOLS*DROWS);
        !          3002:     shuffleList(sCoord, DCOLS*DROWS);
        !          3003:     for (i = 0; i < DCOLS*DROWS && rogue.wpCount < MAX_WAYPOINT_COUNT; i++) {
        !          3004:         x = sCoord[i]/DROWS;
        !          3005:         y = sCoord[i] % DROWS;
        !          3006:         if (!grid[x][y]) {
        !          3007:             getFOVMask(grid, x, y, WAYPOINT_SIGHT_RADIUS * FP_FACTOR, T_OBSTRUCTS_SCENT, 0, false);
        !          3008:             grid[x][y] = true;
        !          3009:             rogue.wpCoordinates[rogue.wpCount][0] = x;
        !          3010:             rogue.wpCoordinates[rogue.wpCount][1] = y;
        !          3011:             rogue.wpCount++;
        !          3012: //            blackOutScreen();
        !          3013: //            dumpLevelToScreen();
        !          3014: //            hiliteCharGrid(grid, &yellow, 50);
        !          3015: //            temporaryMessage("Waypoint coverage so far:", true);
        !          3016:         }
        !          3017:     }
        !          3018:
        !          3019:     for (i=0; i<rogue.wpCount; i++) {
        !          3020:         refreshWaypoint(i);
        !          3021: //        blackOutScreen();
        !          3022: //        dumpLevelToScreen();
        !          3023: //        displayGrid(rogue.wpDistance[i]);
        !          3024: //        temporaryMessage("Waypoint distance map:", true);
        !          3025:     }
        !          3026: }
        !          3027:
        !          3028: void zeroOutGrid(char grid[DCOLS][DROWS]) {
        !          3029:     short i, j;
        !          3030:     for (i=0; i<DCOLS; i++) {
        !          3031:         for (j=0; j<DROWS; j++) {
        !          3032:             grid[i][j] = 0;
        !          3033:         }
        !          3034:     }
        !          3035: }
        !          3036:
        !          3037: short oppositeDirection(short theDir) {
        !          3038:     switch (theDir) {
        !          3039:         case UP:
        !          3040:             return DOWN;
        !          3041:         case DOWN:
        !          3042:             return UP;
        !          3043:         case LEFT:
        !          3044:             return RIGHT;
        !          3045:         case RIGHT:
        !          3046:             return LEFT;
        !          3047:         case UPRIGHT:
        !          3048:             return DOWNLEFT;
        !          3049:         case DOWNLEFT:
        !          3050:             return UPRIGHT;
        !          3051:         case UPLEFT:
        !          3052:             return DOWNRIGHT;
        !          3053:         case DOWNRIGHT:
        !          3054:             return UPLEFT;
        !          3055:         case NO_DIRECTION:
        !          3056:             return NO_DIRECTION;
        !          3057:         default:
        !          3058:             return -1;
        !          3059:     }
        !          3060: }
        !          3061:
        !          3062: // blockingMap is optional.
        !          3063: // Returns the size of the connected zone, and marks visited[][] with the zoneLabel.
        !          3064: short connectCell(short x, short y, short zoneLabel, char blockingMap[DCOLS][DROWS], char zoneMap[DCOLS][DROWS]) {
        !          3065:     enum directions dir;
        !          3066:     short newX, newY, size;
        !          3067:
        !          3068:     zoneMap[x][y] = zoneLabel;
        !          3069:     size = 1;
        !          3070:
        !          3071:     for (dir = 0; dir < 4; dir++) {
        !          3072:         newX = x + nbDirs[dir][0];
        !          3073:         newY = y + nbDirs[dir][1];
        !          3074:
        !          3075:         if (coordinatesAreInMap(newX, newY)
        !          3076:             && zoneMap[newX][newY] == 0
        !          3077:             && (!blockingMap || !blockingMap[newX][newY])
        !          3078:             && cellIsPassableOrDoor(newX, newY)) {
        !          3079:
        !          3080:             size += connectCell(newX, newY, zoneLabel, blockingMap, zoneMap);
        !          3081:         }
        !          3082:     }
        !          3083:     return size;
        !          3084: }
        !          3085:
        !          3086: // Make a zone map of connected passable regions that include at least one passable
        !          3087: // cell that borders the blockingMap if blockingMap blocks. Keep track of the size of each zone.
        !          3088: // Then pretend that the blockingMap no longer blocks, and grow these zones into the resulting area
        !          3089: // (without changing the stored zone sizes). If two or more zones now touch, then we block.
        !          3090: // At that point, return the size in cells of the smallest of all of the touching regions
        !          3091: // (or just 1, i.e. true, if countRegionSize is false). If no zones touch, then we don't block, and we return zero, i.e. false.
        !          3092: short levelIsDisconnectedWithBlockingMap(char blockingMap[DCOLS][DROWS], boolean countRegionSize) {
        !          3093:     char zoneMap[DCOLS][DROWS];
        !          3094:     short i, j, dir, zoneSizes[200], zoneCount, smallestQualifyingZoneSize, borderingZone;
        !          3095:
        !          3096:     zoneCount = 0;
        !          3097:     smallestQualifyingZoneSize = 10000;
        !          3098:     zeroOutGrid(zoneMap);
        !          3099:
        !          3100: //  dumpLevelToScreen();
        !          3101: //  hiliteCharGrid(blockingMap, &omniscienceColor, 100);
        !          3102: //  temporaryMessage("Blocking map:", true);
        !          3103:
        !          3104:     // Map out the zones with the blocking area blocked.
        !          3105:     for (i=1; i<DCOLS-1; i++) {
        !          3106:         for (j=1; j<DROWS-1; j++) {
        !          3107:             if (cellIsPassableOrDoor(i, j) && zoneMap[i][j] == 0 && !blockingMap[i][j]) {
        !          3108:                 for (dir=0; dir<4; dir++) {
        !          3109:                     if (blockingMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]]) {
        !          3110:                         zoneCount++;
        !          3111:                         zoneSizes[zoneCount - 1] = connectCell(i, j, zoneCount, blockingMap, zoneMap);
        !          3112:                         break;
        !          3113:                     }
        !          3114:                 }
        !          3115:             }
        !          3116:         }
        !          3117:     }
        !          3118:
        !          3119:     // Expand the zones into the blocking area.
        !          3120:     for (i=1; i<DCOLS-1; i++) {
        !          3121:         for (j=1; j<DROWS-1; j++) {
        !          3122:             if (blockingMap[i][j] && zoneMap[i][j] == 0 && cellIsPassableOrDoor(i, j)) {
        !          3123:                 for (dir=0; dir<4; dir++) {
        !          3124:                     borderingZone = zoneMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]];
        !          3125:                     if (borderingZone != 0) {
        !          3126:                         connectCell(i, j, borderingZone, NULL, zoneMap);
        !          3127:                         break;
        !          3128:                     }
        !          3129:                 }
        !          3130:             }
        !          3131:         }
        !          3132:     }
        !          3133:
        !          3134:     // Figure out which zones touch.
        !          3135:     for (i=1; i<DCOLS-1; i++) {
        !          3136:         for (j=1; j<DROWS-1; j++) {
        !          3137:             if (zoneMap[i][j] != 0) {
        !          3138:                 for (dir=0; dir<4; dir++) {
        !          3139:                     borderingZone = zoneMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]];
        !          3140:                     if (zoneMap[i][j] != borderingZone && borderingZone != 0) {
        !          3141:                         if (!countRegionSize) {
        !          3142:                             return true;
        !          3143:                         }
        !          3144:                         smallestQualifyingZoneSize = min(smallestQualifyingZoneSize, zoneSizes[zoneMap[i][j] - 1]);
        !          3145:                         smallestQualifyingZoneSize = min(smallestQualifyingZoneSize, zoneSizes[borderingZone - 1]);
        !          3146:                         break;
        !          3147:                     }
        !          3148:                 }
        !          3149:             }
        !          3150:         }
        !          3151:     }
        !          3152:     return (smallestQualifyingZoneSize < 10000 ? smallestQualifyingZoneSize : 0);
        !          3153: }
        !          3154:
        !          3155: void resetDFMessageEligibility() {
        !          3156:     short i;
        !          3157:
        !          3158:     for (i=0; i<NUMBER_DUNGEON_FEATURES; i++) {
        !          3159:         dungeonFeatureCatalog[i].messageDisplayed = false;
        !          3160:     }
        !          3161: }
        !          3162:
        !          3163: boolean fillSpawnMap(enum dungeonLayers layer,
        !          3164:                      enum tileType surfaceTileType,
        !          3165:                      char spawnMap[DCOLS][DROWS],
        !          3166:                      boolean blockedByOtherLayers,
        !          3167:                      boolean refresh,
        !          3168:                      boolean superpriority) {
        !          3169:     short i, j;
        !          3170:     creature *monst;
        !          3171:     item *theItem;
        !          3172:     boolean accomplishedSomething;
        !          3173:
        !          3174:     accomplishedSomething = false;
        !          3175:
        !          3176:     for (i=0; i<DCOLS; i++) {
        !          3177:         for (j=0; j<DROWS; j++) {
        !          3178:             if (    // If it's flagged for building in the spawn map,
        !          3179:                 spawnMap[i][j]
        !          3180:                     // and the new cell doesn't already contain the fill terrain,
        !          3181:                 && pmap[i][j].layers[layer] != surfaceTileType
        !          3182:                     // and the terrain in the layer to be overwritten has a higher priority number (unless superpriority),
        !          3183:                 && (superpriority || tileCatalog[pmap[i][j].layers[layer]].drawPriority >= tileCatalog[surfaceTileType].drawPriority)
        !          3184:                     // and we won't be painting into the surface layer when that cell forbids it,
        !          3185:                 && !(layer == SURFACE && cellHasTerrainFlag(i, j, T_OBSTRUCTS_SURFACE_EFFECTS))
        !          3186:                     // and, if requested, the fill won't violate the priority of the most important terrain in this cell:
        !          3187:                 && (!blockedByOtherLayers || tileCatalog[pmap[i][j].layers[highestPriorityLayer(i, j, true)]].drawPriority >= tileCatalog[surfaceTileType].drawPriority)
        !          3188:                 ) {
        !          3189:
        !          3190:                 if ((tileCatalog[surfaceTileType].flags & T_IS_FIRE)
        !          3191:                     && !(tileCatalog[pmap[i][j].layers[layer]].flags & T_IS_FIRE)) {
        !          3192:                     pmap[i][j].flags |= CAUGHT_FIRE_THIS_TURN;
        !          3193:                 }
        !          3194:
        !          3195:                 if ((tileCatalog[pmap[i][j].layers[layer]].flags & T_PATHING_BLOCKER)
        !          3196:                     != (tileCatalog[surfaceTileType].flags & T_PATHING_BLOCKER)) {
        !          3197:
        !          3198:                     rogue.staleLoopMap = true;
        !          3199:                 }
        !          3200:
        !          3201:                 pmap[i][j].layers[layer] = surfaceTileType; // Place the terrain!
        !          3202:                 accomplishedSomething = true;
        !          3203:
        !          3204:                 if (refresh) {
        !          3205:                     refreshDungeonCell(i, j);
        !          3206:                     if (player.xLoc == i && player.yLoc == j && !player.status[STATUS_LEVITATING] && refresh) {
        !          3207:                         flavorMessage(tileFlavor(player.xLoc, player.yLoc));
        !          3208:                     }
        !          3209:                     if (pmap[i][j].flags & (HAS_MONSTER)) {
        !          3210:                         monst = monsterAtLoc(i, j);
        !          3211:                         applyInstantTileEffectsToCreature(monst);
        !          3212:                         if (rogue.gameHasEnded) {
        !          3213:                             return true;
        !          3214:                         }
        !          3215:                     }
        !          3216:                     if (tileCatalog[surfaceTileType].flags & T_IS_FIRE) {
        !          3217:                         if (pmap[i][j].flags & HAS_ITEM) {
        !          3218:                             theItem = itemAtLoc(i, j);
        !          3219:                             if (theItem->flags & ITEM_FLAMMABLE) {
        !          3220:                                 burnItem(theItem);
        !          3221:                             }
        !          3222:                         }
        !          3223:                     }
        !          3224:                 }
        !          3225:             } else {
        !          3226:                 spawnMap[i][j] = false; // so that the spawnmap reflects what actually got built
        !          3227:             }
        !          3228:         }
        !          3229:     }
        !          3230:     return accomplishedSomething;
        !          3231: }
        !          3232:
        !          3233: void spawnMapDF(short x, short y,
        !          3234:                 enum tileType propagationTerrain,
        !          3235:                 boolean requirePropTerrain,
        !          3236:                 short startProb,
        !          3237:                 short probDec,
        !          3238:                 char spawnMap[DCOLS][DROWS]) {
        !          3239:
        !          3240:     short i, j, dir, t, x2, y2;
        !          3241:     boolean madeChange;
        !          3242:
        !          3243:     spawnMap[x][y] = t = 1; // incremented before anything else happens
        !          3244:
        !          3245:     madeChange = true;
        !          3246:
        !          3247:     while (madeChange && startProb > 0) {
        !          3248:         madeChange = false;
        !          3249:         t++;
        !          3250:         for (i = 0; i < DCOLS; i++) {
        !          3251:             for (j=0; j < DROWS; j++) {
        !          3252:                 if (spawnMap[i][j] == t - 1) {
        !          3253:                     for (dir = 0; dir < 4; dir++) {
        !          3254:                         x2 = i + nbDirs[dir][0];
        !          3255:                         y2 = j + nbDirs[dir][1];
        !          3256:                         if (coordinatesAreInMap(x2, y2)
        !          3257:                             && (!requirePropTerrain || (propagationTerrain > 0 && cellHasTerrainType(x2, y2, propagationTerrain)))
        !          3258:                             && (!cellHasTerrainFlag(x2, y2, T_OBSTRUCTS_SURFACE_EFFECTS) || (propagationTerrain > 0 && cellHasTerrainType(x2, y2, propagationTerrain)))
        !          3259:                             && rand_percent(startProb)) {
        !          3260:
        !          3261:                             spawnMap[x2][y2] = t;
        !          3262:                             madeChange = true;
        !          3263:                         }
        !          3264:                     }
        !          3265:                 }
        !          3266:             }
        !          3267:         }
        !          3268:         startProb -= probDec;
        !          3269:         if (t > 100) {
        !          3270:             for (i = 0; i < DCOLS; i++) {
        !          3271:                 for (j=0; j < DROWS; j++) {
        !          3272:                     if (spawnMap[i][j] == t) {
        !          3273:                         spawnMap[i][j] = 2;
        !          3274:                     } else if (spawnMap[i][j] > 0) {
        !          3275:                         spawnMap[i][j] = 1;
        !          3276:                     }
        !          3277:                 }
        !          3278:             }
        !          3279:             t = 2;
        !          3280:         }
        !          3281:     }
        !          3282:     if (requirePropTerrain && !cellHasTerrainType(x, y, propagationTerrain)) {
        !          3283:         spawnMap[x][y] = 0;
        !          3284:     }
        !          3285: }
        !          3286:
        !          3287: void evacuateCreatures(char blockingMap[DCOLS][DROWS]) {
        !          3288:     short i, j, newLoc[2];
        !          3289:     creature *monst;
        !          3290:
        !          3291:     for (i=0; i<DCOLS; i++) {
        !          3292:         for (j=0; j<DROWS; j++) {
        !          3293:             if (blockingMap[i][j]
        !          3294:                 && (pmap[i][j].flags & (HAS_MONSTER | HAS_PLAYER))) {
        !          3295:
        !          3296:                 monst = monsterAtLoc(i, j);
        !          3297:                 getQualifyingLocNear(newLoc,
        !          3298:                                      i, j,
        !          3299:                                      true,
        !          3300:                                      blockingMap,
        !          3301:                                      forbiddenFlagsForMonster(&(monst->info)),
        !          3302:                                      (HAS_MONSTER | HAS_PLAYER),
        !          3303:                                      false,
        !          3304:                                      false);
        !          3305:                 monst->xLoc = newLoc[0];
        !          3306:                 monst->yLoc = newLoc[1];
        !          3307:                 pmap[i][j].flags &= ~(HAS_MONSTER | HAS_PLAYER);
        !          3308:                 pmap[newLoc[0]][newLoc[1]].flags |= (monst == &player ? HAS_PLAYER : HAS_MONSTER);
        !          3309:             }
        !          3310:         }
        !          3311:     }
        !          3312: }
        !          3313:
        !          3314: // returns whether the feature was successfully generated (false if we aborted because of blocking)
        !          3315: boolean spawnDungeonFeature(short x, short y, dungeonFeature *feat, boolean refreshCell, boolean abortIfBlocking) {
        !          3316:     short i, j, layer;
        !          3317:     char blockingMap[DCOLS][DROWS];
        !          3318:     boolean blocking;
        !          3319:     boolean succeeded;
        !          3320:     creature *monst;
        !          3321:
        !          3322:     if ((feat->flags & DFF_RESURRECT_ALLY)
        !          3323:         && !resurrectAlly(x, y)) {
        !          3324:         return false;
        !          3325:     }
        !          3326:
        !          3327:     if (feat->description[0] && !feat->messageDisplayed && playerCanSee(x, y)) {
        !          3328:         feat->messageDisplayed = true;
        !          3329:         message(feat->description, false);
        !          3330:     }
        !          3331:
        !          3332:     zeroOutGrid(blockingMap);
        !          3333:
        !          3334:     // Blocking keeps track of whether to abort if it turns out that the DF would obstruct the level.
        !          3335:     blocking = ((abortIfBlocking
        !          3336:                  && !(feat->flags & DFF_PERMIT_BLOCKING)
        !          3337:                  && ((tileCatalog[feat->tile].flags & (T_PATHING_BLOCKER))
        !          3338:                      || (feat->flags & DFF_TREAT_AS_BLOCKING))) ? true : false);
        !          3339:
        !          3340:     if (feat->tile) {
        !          3341:         if (feat->layer == GAS) {
        !          3342:             pmap[x][y].volume += feat->startProbability;
        !          3343:             pmap[x][y].layers[GAS] = feat->tile;
        !          3344:             if (refreshCell) {
        !          3345:                 refreshDungeonCell(x, y);
        !          3346:             }
        !          3347:             succeeded = true;
        !          3348:         } else {
        !          3349:             spawnMapDF(x, y,
        !          3350:                        feat->propagationTerrain,
        !          3351:                        (feat->propagationTerrain ? true : false),
        !          3352:                        feat->startProbability,
        !          3353:                        feat->probabilityDecrement,
        !          3354:                        blockingMap);
        !          3355:             if (!blocking || !levelIsDisconnectedWithBlockingMap(blockingMap, false)) {
        !          3356:                 if (feat->flags & DFF_EVACUATE_CREATURES_FIRST) { // first, evacuate creatures if necessary, so that they do not re-trigger the tile.
        !          3357:                     evacuateCreatures(blockingMap);
        !          3358:                 }
        !          3359:
        !          3360:                 //succeeded = fillSpawnMap(feat->layer, feat->tile, blockingMap, (feat->flags & DFF_BLOCKED_BY_OTHER_LAYERS), refreshCell, (feat->flags & DFF_SUPERPRIORITY));
        !          3361:                 fillSpawnMap(feat->layer,
        !          3362:                              feat->tile,
        !          3363:                              blockingMap,
        !          3364:                              (feat->flags & DFF_BLOCKED_BY_OTHER_LAYERS),
        !          3365:                              refreshCell,
        !          3366:                              (feat->flags & DFF_SUPERPRIORITY)); // this can tweak the spawn map too
        !          3367:                 succeeded = true; // fail ONLY if we blocked the level. We succeed even if, thanks to priority, nothing gets built.
        !          3368:             } else {
        !          3369:                 succeeded = false;
        !          3370:             }
        !          3371:         }
        !          3372:     } else {
        !          3373:         blockingMap[x][y] = true;
        !          3374:         succeeded = true; // Automatically succeed if there is no terrain to place.
        !          3375:         if (feat->flags & DFF_EVACUATE_CREATURES_FIRST) { // first, evacuate creatures if necessary, so that they do not re-trigger the tile.
        !          3376:             evacuateCreatures(blockingMap);
        !          3377:         }
        !          3378:     }
        !          3379:
        !          3380:     if (succeeded && (feat->flags & DFF_CLEAR_OTHER_TERRAIN)) {
        !          3381:         for (i=0; i<DCOLS; i++) {
        !          3382:             for (j=0; j<DROWS; j++) {
        !          3383:                 if (blockingMap[i][j]) {
        !          3384:                     for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
        !          3385:                         if (layer != feat->layer && layer != GAS) {
        !          3386:                             pmap[i][j].layers[layer] = (layer == DUNGEON ? FLOOR : NOTHING);
        !          3387:                         }
        !          3388:                     }
        !          3389:                 }
        !          3390:             }
        !          3391:         }
        !          3392:     }
        !          3393:
        !          3394:     if (succeeded) {
        !          3395:         if ((feat->flags & DFF_AGGRAVATES_MONSTERS) && feat->effectRadius) {
        !          3396:             aggravateMonsters(feat->effectRadius, x, y, &gray);
        !          3397:         }
        !          3398:         if (refreshCell && feat->flashColor && feat->effectRadius) {
        !          3399:             colorFlash(feat->flashColor, 0, (IN_FIELD_OF_VIEW | CLAIRVOYANT_VISIBLE), 4, feat->effectRadius, x, y);
        !          3400:         }
        !          3401:         if (refreshCell && feat->lightFlare) {
        !          3402:             createFlare(x, y, feat->lightFlare);
        !          3403:         }
        !          3404:     }
        !          3405:
        !          3406:     if (refreshCell
        !          3407:         && (tileCatalog[feat->tile].flags & (T_IS_FIRE | T_AUTO_DESCENT))
        !          3408:         && cellHasTerrainFlag(player.xLoc, player.yLoc, (T_IS_FIRE | T_AUTO_DESCENT))) {
        !          3409:
        !          3410:         applyInstantTileEffectsToCreature(&player);
        !          3411:     }
        !          3412:     if (rogue.gameHasEnded) {
        !          3413:         return succeeded;
        !          3414:     }
        !          3415:     //  if (succeeded && feat->description[0] && !feat->messageDisplayed && playerCanSee(x, y)) {
        !          3416:     //      feat->messageDisplayed = true;
        !          3417:     //      message(feat->description, false);
        !          3418:     //  }
        !          3419:     if (succeeded) {
        !          3420:         if (feat->subsequentDF) {
        !          3421:             if (feat->flags & DFF_SUBSEQ_EVERYWHERE) {
        !          3422:                 for (i=0; i<DCOLS; i++) {
        !          3423:                     for (j=0; j<DROWS; j++) {
        !          3424:                         if (blockingMap[i][j]) {
        !          3425:                             spawnDungeonFeature(i, j, &dungeonFeatureCatalog[feat->subsequentDF], refreshCell, abortIfBlocking);
        !          3426:                         }
        !          3427:                     }
        !          3428:                 }
        !          3429:             } else {
        !          3430:                 spawnDungeonFeature(x, y, &dungeonFeatureCatalog[feat->subsequentDF], refreshCell, abortIfBlocking);
        !          3431:             }
        !          3432:         }
        !          3433:         if (feat->tile
        !          3434:             && (tileCatalog[feat->tile].flags & (T_IS_DEEP_WATER | T_LAVA_INSTA_DEATH | T_AUTO_DESCENT))) {
        !          3435:
        !          3436:             rogue.updatedMapToShoreThisTurn = false;
        !          3437:         }
        !          3438:
        !          3439:         // awaken dormant creatures?
        !          3440:         if (feat->flags & DFF_ACTIVATE_DORMANT_MONSTER) {
        !          3441:             for (monst = dormantMonsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
        !          3442:                 if (monst->xLoc == x && monst->yLoc == y || blockingMap[monst->xLoc][monst->yLoc]) {
        !          3443:                     // found it!
        !          3444:                     toggleMonsterDormancy(monst);
        !          3445:                     monst = dormantMonsters;
        !          3446:                 }
        !          3447:             }
        !          3448:         }
        !          3449:     }
        !          3450:     return succeeded;
        !          3451: }
        !          3452:
        !          3453: void restoreMonster(creature *monst, short **mapToStairs, short **mapToPit) {
        !          3454:     short i, *x, *y, turnCount;//, loc[2];
        !          3455:     creature *leader;
        !          3456:     boolean foundLeader = false;
        !          3457:     short **theMap;
        !          3458:     enum directions dir;
        !          3459:
        !          3460:     x = &(monst->xLoc);
        !          3461:     y = &(monst->yLoc);
        !          3462:
        !          3463:     if (monst->status[STATUS_ENTERS_LEVEL_IN] > 0) {
        !          3464:         if (monst->bookkeepingFlags & (MB_APPROACHING_PIT)) {
        !          3465:             theMap = mapToPit;
        !          3466:         } else {
        !          3467:             theMap = mapToStairs;
        !          3468:         }
        !          3469:
        !          3470:         if(rogue.patchVersion >= 3) {
        !          3471:             pmap[*x][*y].flags &= ~HAS_MONSTER;
        !          3472:         }
        !          3473:         if (theMap) {
        !          3474:             // STATUS_ENTERS_LEVEL_IN accounts for monster speed; convert back to map distance and subtract from distance to stairs
        !          3475:             turnCount = rogue.patchVersion < 3 ? ((theMap[monst->xLoc][monst->yLoc] * monst->movementSpeed / 100) - monst->status[STATUS_ENTERS_LEVEL_IN])
        !          3476:                         : (theMap[monst->xLoc][monst->yLoc] - (monst->status[STATUS_ENTERS_LEVEL_IN] * 100 / monst->movementSpeed));
        !          3477:             for (i=0; i < turnCount; i++) {
        !          3478:                 if ((dir = nextStep(theMap, monst->xLoc, monst->yLoc, NULL, true)) != NO_DIRECTION) {
        !          3479:                     monst->xLoc += nbDirs[dir][0];
        !          3480:                     monst->yLoc += nbDirs[dir][1];
        !          3481:                 } else {
        !          3482:                     break;
        !          3483:                 }
        !          3484:             }
        !          3485:         }
        !          3486:         monst->bookkeepingFlags |= MB_PREPLACED;
        !          3487:     }
        !          3488:
        !          3489:     if ((pmap[*x][*y].flags & (HAS_PLAYER | HAS_STAIRS))
        !          3490:         || (monst->bookkeepingFlags & MB_PREPLACED)) {
        !          3491:
        !          3492:         if (!(monst->bookkeepingFlags & MB_PREPLACED)) {
        !          3493:             // (If if it's preplaced, it won't have set the HAS_MONSTER flag in the first place,
        !          3494:             // so clearing it might screw up an existing monster.)
        !          3495:             pmap[*x][*y].flags &= ~HAS_MONSTER;
        !          3496:         }
        !          3497:         getQualifyingPathLocNear(x, y, *x, *y, true, T_DIVIDES_LEVEL & avoidedFlagsForMonster(&(monst->info)), 0,
        !          3498:                                  avoidedFlagsForMonster(&(monst->info)), (HAS_MONSTER | HAS_PLAYER | HAS_STAIRS), true);
        !          3499:     }
        !          3500:     pmap[*x][*y].flags |= HAS_MONSTER;
        !          3501:     monst->bookkeepingFlags &= ~(MB_PREPLACED | MB_APPROACHING_DOWNSTAIRS | MB_APPROACHING_UPSTAIRS | MB_APPROACHING_PIT | MB_ABSORBING);
        !          3502:     monst->status[STATUS_ENTERS_LEVEL_IN] = 0;
        !          3503:     monst->corpseAbsorptionCounter = 0;
        !          3504:
        !          3505:     if ((monst->bookkeepingFlags & MB_SUBMERGED) && !cellHasTMFlag(*x, *y, TM_ALLOWS_SUBMERGING)) {
        !          3506:         monst->bookkeepingFlags &= ~MB_SUBMERGED;
        !          3507:     }
        !          3508:
        !          3509:     if (monst->bookkeepingFlags & MB_FOLLOWER) {
        !          3510:         // is the leader on the same level?
        !          3511:         for (leader = monsters->nextCreature; leader != NULL; leader = leader->nextCreature) {
        !          3512:             if (leader == monst->leader) {
        !          3513:                 foundLeader = true;
        !          3514:                 break;
        !          3515:             }
        !          3516:         }
        !          3517:         // if not, it is time to spread your wings and fly solo
        !          3518:         if (!foundLeader) {
        !          3519:             monst->bookkeepingFlags &= ~MB_FOLLOWER;
        !          3520:             monst->leader = NULL;
        !          3521:         }
        !          3522:     }
        !          3523: }
        !          3524:
        !          3525: void restoreItem(item *theItem) {
        !          3526:     short *x, *y, loc[2];
        !          3527:     x = &(theItem->xLoc);
        !          3528:     y = &(theItem->yLoc);
        !          3529:
        !          3530:     if (theItem->flags & ITEM_PREPLACED) {
        !          3531:         theItem->flags &= ~ITEM_PREPLACED;
        !          3532:         getQualifyingLocNear(loc, *x, *y, true, 0, (T_OBSTRUCTS_ITEMS | T_AUTO_DESCENT | T_IS_DEEP_WATER | T_LAVA_INSTA_DEATH),
        !          3533:                              (HAS_MONSTER | HAS_ITEM | HAS_STAIRS), true, false);
        !          3534:         *x = loc[0];
        !          3535:         *y = loc[1];
        !          3536:     }
        !          3537:     pmap[*x][*y].flags |= HAS_ITEM;
        !          3538:     if (theItem->flags & ITEM_MAGIC_DETECTED && itemMagicPolarity(theItem)) {
        !          3539:         pmap[*x][*y].flags |= ITEM_DETECTED;
        !          3540:     }
        !          3541: }
        !          3542:
        !          3543: // Returns true iff the location is a plain wall, three of the four cardinal neighbors are walls, the remaining cardinal neighbor
        !          3544: // is not a pathing blocker, the two diagonals between the three cardinal walls are also walls, and none of the eight neighbors are in machines.
        !          3545: boolean validStairLoc(short x, short y) {
        !          3546:     short newX, newY, dir, neighborWallCount;
        !          3547:
        !          3548:     if (x < 1 || x >= DCOLS - 1 || y < 1 || y >= DROWS - 1 || pmap[x][y].layers[DUNGEON] != WALL) {
        !          3549:         return false;
        !          3550:     }
        !          3551:
        !          3552:     for (dir=0; dir< DIRECTION_COUNT; dir++) {
        !          3553:         newX = x + nbDirs[dir][0];
        !          3554:         newY = y + nbDirs[dir][1];
        !          3555:         if (pmap[newX][newY].flags & IS_IN_MACHINE) {
        !          3556:             return false;
        !          3557:         }
        !          3558:     }
        !          3559:
        !          3560:     neighborWallCount = 0;
        !          3561:     for (dir=0; dir<4; dir++) {
        !          3562:         newX = x + nbDirs[dir][0];
        !          3563:         newY = y + nbDirs[dir][1];
        !          3564:
        !          3565:         if (cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)) {
        !          3566:             // neighbor is a wall
        !          3567:             neighborWallCount++;
        !          3568:         } else {
        !          3569:             // neighbor is not a wall
        !          3570:             if (cellHasTerrainFlag(newX, newY, T_PATHING_BLOCKER)
        !          3571:                 || passableArcCount(newX, newY) >= 2) {
        !          3572:                 return false;
        !          3573:             }
        !          3574:             // now check the two diagonals between the walls
        !          3575:
        !          3576:             newX = x - nbDirs[dir][0] + nbDirs[dir][1];
        !          3577:             newY = y - nbDirs[dir][1] + nbDirs[dir][0];
        !          3578:             if (!cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)) {
        !          3579:                 return false;
        !          3580:             }
        !          3581:
        !          3582:             newX = x - nbDirs[dir][0] - nbDirs[dir][1];
        !          3583:             newY = y - nbDirs[dir][1] - nbDirs[dir][0];
        !          3584:             if (!cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)) {
        !          3585:                 return false;
        !          3586:             }
        !          3587:         }
        !          3588:     }
        !          3589:     if (neighborWallCount != 3) {
        !          3590:         return false;
        !          3591:     }
        !          3592:     return true;
        !          3593: }
        !          3594:
        !          3595: // The walls on either side become torches. Any adjacent granite then becomes top_wall. All wall neighbors are un-tunnelable.
        !          3596: // Grid is zeroed out within 5 spaces in all directions.
        !          3597: void prepareForStairs(short x, short y, char grid[DCOLS][DROWS]) {
        !          3598:     short newX, newY, dir;
        !          3599:
        !          3600:     // Add torches to either side.
        !          3601:     for (dir=0; dir<4; dir++) {
        !          3602:         if (!cellHasTerrainFlag(x + nbDirs[dir][0], y + nbDirs[dir][1], T_OBSTRUCTS_PASSABILITY)) {
        !          3603:             newX = x - nbDirs[dir][1];
        !          3604:             newY = y - nbDirs[dir][0];
        !          3605:             pmap[newX][newY].layers[DUNGEON] = TORCH_WALL;
        !          3606:             newX = x + nbDirs[dir][1];
        !          3607:             newY = y + nbDirs[dir][0];
        !          3608:             pmap[newX][newY].layers[DUNGEON] = TORCH_WALL;
        !          3609:             break;
        !          3610:         }
        !          3611:     }
        !          3612:     // Expose granite.
        !          3613:     for (dir=0; dir< DIRECTION_COUNT; dir++) {
        !          3614:         newX = x + nbDirs[dir][0];
        !          3615:         newY = y + nbDirs[dir][1];
        !          3616:         if (pmap[newX][newY].layers[DUNGEON] == GRANITE) {
        !          3617:             pmap[newX][newY].layers[DUNGEON] = WALL;
        !          3618:         }
        !          3619:         if (cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)) {
        !          3620:             pmap[newX][newY].flags |= IMPREGNABLE;
        !          3621:         }
        !          3622:     }
        !          3623:     // Zero out grid in the vicinity.
        !          3624:     for (newX = max(0, x - 5); newX < min(DCOLS, x + 5); newX++) {
        !          3625:         for (newY = max(0, y - 5); newY < min(DROWS, y + 5); newY++) {
        !          3626:             grid[newX][newY] = false;
        !          3627:         }
        !          3628:     }
        !          3629: }
        !          3630:
        !          3631: // Places the player, monsters, items and stairs.
        !          3632: void initializeLevel() {
        !          3633:     short i, j, dir;
        !          3634:     short upLoc[2], downLoc[2], **mapToStairs, **mapToPit;
        !          3635:     creature *monst;
        !          3636:     item *theItem;
        !          3637:     char grid[DCOLS][DROWS];
        !          3638:     short n = rogue.depthLevel - 1;
        !          3639:
        !          3640:     // Place the stairs.
        !          3641:
        !          3642:     for (i=0; i < DCOLS; i++) {
        !          3643:         for (j=0; j < DROWS; j++) {
        !          3644:             grid[i][j] = validStairLoc(i, j);
        !          3645:         }
        !          3646:     }
        !          3647:
        !          3648:     if (D_INSPECT_LEVELGEN) {
        !          3649:         dumpLevelToScreen();
        !          3650:         hiliteCharGrid(grid, &teal, 100);
        !          3651:         temporaryMessage("Stair location candidates:", true);
        !          3652:     }
        !          3653:
        !          3654:     if (getQualifyingGridLocNear(downLoc, levels[n].downStairsLoc[0], levels[n].downStairsLoc[1], grid, false)) {
        !          3655:         prepareForStairs(downLoc[0], downLoc[1], grid);
        !          3656:     } else {
        !          3657:         getQualifyingLocNear(downLoc, levels[n].downStairsLoc[0], levels[n].downStairsLoc[1], false, 0,
        !          3658:                              (T_OBSTRUCTS_PASSABILITY | T_OBSTRUCTS_ITEMS | T_AUTO_DESCENT | T_IS_DEEP_WATER | T_LAVA_INSTA_DEATH | T_IS_DF_TRAP),
        !          3659:                              (HAS_MONSTER | HAS_ITEM | HAS_STAIRS | IS_IN_MACHINE), true, false);
        !          3660:     }
        !          3661:
        !          3662:     if (rogue.depthLevel == DEEPEST_LEVEL) {
        !          3663:         pmap[downLoc[0]][downLoc[1]].layers[DUNGEON] = DUNGEON_PORTAL;
        !          3664:     } else {
        !          3665:         pmap[downLoc[0]][downLoc[1]].layers[DUNGEON] = DOWN_STAIRS;
        !          3666:     }
        !          3667:     pmap[downLoc[0]][downLoc[1]].layers[LIQUID]     = NOTHING;
        !          3668:     pmap[downLoc[0]][downLoc[1]].layers[SURFACE]    = NOTHING;
        !          3669:
        !          3670:     if (!levels[n+1].visited) {
        !          3671:         levels[n+1].upStairsLoc[0] = downLoc[0];
        !          3672:         levels[n+1].upStairsLoc[1] = downLoc[1];
        !          3673:     }
        !          3674:     levels[n].downStairsLoc[0] = downLoc[0];
        !          3675:     levels[n].downStairsLoc[1] = downLoc[1];
        !          3676:
        !          3677:     if (getQualifyingGridLocNear(upLoc, levels[n].upStairsLoc[0], levels[n].upStairsLoc[1], grid, false)) {
        !          3678:         prepareForStairs(upLoc[0], upLoc[1], grid);
        !          3679:     } else { // Hopefully this never happens.
        !          3680:         getQualifyingLocNear(upLoc, levels[n].upStairsLoc[0], levels[n].upStairsLoc[1], false, 0,
        !          3681:                              (T_OBSTRUCTS_PASSABILITY | T_OBSTRUCTS_ITEMS | T_AUTO_DESCENT | T_IS_DEEP_WATER | T_LAVA_INSTA_DEATH | T_IS_DF_TRAP),
        !          3682:                              (HAS_MONSTER | HAS_ITEM | HAS_STAIRS | IS_IN_MACHINE), true, false);
        !          3683:     }
        !          3684:
        !          3685:     levels[n].upStairsLoc[0] = upLoc[0];
        !          3686:     levels[n].upStairsLoc[1] = upLoc[1];
        !          3687:
        !          3688:     if (rogue.depthLevel == 1) {
        !          3689:         pmap[upLoc[0]][upLoc[1]].layers[DUNGEON] = DUNGEON_EXIT;
        !          3690:     } else {
        !          3691:         pmap[upLoc[0]][upLoc[1]].layers[DUNGEON] = UP_STAIRS;
        !          3692:     }
        !          3693:     pmap[upLoc[0]][upLoc[1]].layers[LIQUID] = NOTHING;
        !          3694:     pmap[upLoc[0]][upLoc[1]].layers[SURFACE] = NOTHING;
        !          3695:
        !          3696:     rogue.downLoc[0] = downLoc[0];
        !          3697:     rogue.downLoc[1] = downLoc[1];
        !          3698:     pmap[downLoc[0]][downLoc[1]].flags |= HAS_STAIRS;
        !          3699:     rogue.upLoc[0] = upLoc[0];
        !          3700:     rogue.upLoc[1] = upLoc[1];
        !          3701:     pmap[upLoc[0]][upLoc[1]].flags |= HAS_STAIRS;
        !          3702:
        !          3703:     if (!levels[rogue.depthLevel-1].visited) {
        !          3704:
        !          3705:         // Run a field of view check from up stairs so that monsters do not spawn within sight of it.
        !          3706:         for (dir=0; dir<4; dir++) {
        !          3707:             if (coordinatesAreInMap(upLoc[0] + nbDirs[dir][0], upLoc[1] + nbDirs[dir][1])
        !          3708:                 && !cellHasTerrainFlag(upLoc[0] + nbDirs[dir][0], upLoc[1] + nbDirs[dir][1], T_OBSTRUCTS_PASSABILITY)) {
        !          3709:
        !          3710:                 upLoc[0] += nbDirs[dir][0];
        !          3711:                 upLoc[1] += nbDirs[dir][1];
        !          3712:                 break;
        !          3713:             }
        !          3714:         }
        !          3715:         zeroOutGrid(grid);
        !          3716:         getFOVMask(grid, upLoc[0], upLoc[1], max(DCOLS, DROWS) * FP_FACTOR, (T_OBSTRUCTS_VISION), 0, false);
        !          3717:         for (i=0; i<DCOLS; i++) {
        !          3718:             for (j=0; j<DROWS; j++) {
        !          3719:                 if (grid[i][j]) {
        !          3720:                     pmap[i][j].flags |= IN_FIELD_OF_VIEW;
        !          3721:                 }
        !          3722:             }
        !          3723:         }
        !          3724:         populateItems(upLoc[0], upLoc[1]);
        !          3725:         populateMonsters();
        !          3726:     }
        !          3727:
        !          3728:     // Restore items that fell from the previous depth.
        !          3729:     for (theItem = floorItems->nextItem; theItem != NULL; theItem = theItem->nextItem) {
        !          3730:         restoreItem(theItem);
        !          3731:     }
        !          3732:
        !          3733:     // Restore creatures that fell from the previous depth or that have been pathing toward the stairs.
        !          3734:     mapToStairs = allocGrid();
        !          3735:     fillGrid(mapToStairs, 0);
        !          3736:     mapToPit = allocGrid();
        !          3737:     fillGrid(mapToPit, 0);
        !          3738:     calculateDistances(mapToStairs, player.xLoc, player.yLoc, T_PATHING_BLOCKER, NULL, true, true);
        !          3739:     calculateDistances(mapToPit,
        !          3740:                        levels[rogue.depthLevel - 1].playerExitedVia[0],
        !          3741:                        levels[rogue.depthLevel - 1].playerExitedVia[1],
        !          3742:                        T_PATHING_BLOCKER,
        !          3743:                        NULL,
        !          3744:                        true,
        !          3745:                        true);
        !          3746:     for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
        !          3747:         restoreMonster(monst, mapToStairs, mapToPit);
        !          3748:     }
        !          3749:     freeGrid(mapToStairs);
        !          3750:     freeGrid(mapToPit);
        !          3751: }
        !          3752:
        !          3753: // fills (*x, *y) with the coordinates of a random cell with
        !          3754: // no creatures, items or stairs and with either a matching liquid and dungeon type
        !          3755: // or at least one layer of type terrainType.
        !          3756: // A dungeon, liquid type of -1 will match anything.
        !          3757: boolean randomMatchingLocation(short *x, short *y, short dungeonType, short liquidType, short terrainType) {
        !          3758:     short failsafeCount = 0;
        !          3759:     do {
        !          3760:         failsafeCount++;
        !          3761:         *x = rand_range(0, DCOLS - 1);
        !          3762:         *y = rand_range(0, DROWS - 1);
        !          3763:     } while (failsafeCount < 500 && ((terrainType >= 0 && !cellHasTerrainType(*x, *y, terrainType))
        !          3764:                                      || (((dungeonType >= 0 && pmap[*x][*y].layers[DUNGEON] != dungeonType) || (liquidType >= 0 && pmap[*x][*y].layers[LIQUID] != liquidType)) && terrainType < 0)
        !          3765:                                      || (pmap[*x][*y].flags & (HAS_PLAYER | HAS_MONSTER | HAS_STAIRS | HAS_ITEM | IS_IN_MACHINE))
        !          3766:                                      || (terrainType < 0 && !(tileCatalog[dungeonType].flags & T_OBSTRUCTS_ITEMS)
        !          3767:                                          && cellHasTerrainFlag(*x, *y, T_OBSTRUCTS_ITEMS))));
        !          3768:     if (failsafeCount >= 500) {
        !          3769:         return false;
        !          3770:     }
        !          3771:     return true;
        !          3772: }

CVSweb