[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

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