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

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

1.1     ! rubenllo    1: /*
        !             2:  *  Movement.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: void playerRuns(short direction) {
        !            28:     short newX, newY, dir;
        !            29:     boolean cardinalPassability[4];
        !            30:
        !            31:     rogue.disturbed = (player.status[STATUS_CONFUSED] ? true : false);
        !            32:
        !            33:     for (dir = 0; dir < 4; dir++) {
        !            34:         newX = player.xLoc + nbDirs[dir][0];
        !            35:         newY = player.yLoc + nbDirs[dir][1];
        !            36:         cardinalPassability[dir] = monsterAvoids(&player, newX, newY);
        !            37:     }
        !            38:
        !            39:     while (!rogue.disturbed) {
        !            40:         if (!playerMoves(direction)) {
        !            41:             rogue.disturbed = true;
        !            42:             break;
        !            43:         }
        !            44:
        !            45:         newX = player.xLoc + nbDirs[direction][0];
        !            46:         newY = player.yLoc + nbDirs[direction][1];
        !            47:         if (!coordinatesAreInMap(newX, newY)
        !            48:             || monsterAvoids(&player, newX, newY)) {
        !            49:
        !            50:             rogue.disturbed = true;
        !            51:         }
        !            52:         if (isDisturbed(player.xLoc, player.yLoc)) {
        !            53:             rogue.disturbed = true;
        !            54:         } else if (direction < 4) {
        !            55:             for (dir = 0; dir < 4; dir++) {
        !            56:                 newX = player.xLoc + nbDirs[dir][0];
        !            57:                 newY = player.yLoc + nbDirs[dir][1];
        !            58:                 if (cardinalPassability[dir] != monsterAvoids(&player, newX, newY)
        !            59:                     && !(nbDirs[dir][0] + nbDirs[direction][0] == 0 &&
        !            60:                          nbDirs[dir][1] + nbDirs[direction][1] == 0)) {
        !            61:                         // dir is not the x-opposite or y-opposite of direction
        !            62:                     rogue.disturbed = true;
        !            63:                 }
        !            64:             }
        !            65:         }
        !            66:     }
        !            67:     updateFlavorText();
        !            68: }
        !            69:
        !            70: enum dungeonLayers highestPriorityLayer(short x, short y, boolean skipGas) {
        !            71:     short bestPriority = 10000;
        !            72:     enum dungeonLayers tt, best = 0;
        !            73:
        !            74:     for (tt = 0; tt < NUMBER_TERRAIN_LAYERS; tt++) {
        !            75:         if (tt == GAS && skipGas) {
        !            76:             continue;
        !            77:         }
        !            78:         if (pmap[x][y].layers[tt] && tileCatalog[pmap[x][y].layers[tt]].drawPriority < bestPriority) {
        !            79:             bestPriority = tileCatalog[pmap[x][y].layers[tt]].drawPriority;
        !            80:             best = tt;
        !            81:         }
        !            82:     }
        !            83:     return best;
        !            84: }
        !            85:
        !            86: enum dungeonLayers layerWithTMFlag(short x, short y, unsigned long flag) {
        !            87:     enum dungeonLayers layer;
        !            88:
        !            89:     for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
        !            90:         if (tileCatalog[pmap[x][y].layers[layer]].mechFlags & flag) {
        !            91:             return layer;
        !            92:         }
        !            93:     }
        !            94:     return NO_LAYER;
        !            95: }
        !            96:
        !            97: enum dungeonLayers layerWithFlag(short x, short y, unsigned long flag) {
        !            98:     enum dungeonLayers layer;
        !            99:
        !           100:     for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
        !           101:         if (tileCatalog[pmap[x][y].layers[layer]].flags & flag) {
        !           102:             return layer;
        !           103:         }
        !           104:     }
        !           105:     return NO_LAYER;
        !           106: }
        !           107:
        !           108: // Retrieves a pointer to the flavor text of the highest-priority terrain at the given location
        !           109: char *tileFlavor(short x, short y) {
        !           110:     return tileCatalog[pmap[x][y].layers[highestPriorityLayer(x, y, false)]].flavorText;
        !           111: }
        !           112:
        !           113: // Retrieves a pointer to the description text of the highest-priority terrain at the given location
        !           114: char *tileText(short x, short y) {
        !           115:     return tileCatalog[pmap[x][y].layers[highestPriorityLayer(x, y, false)]].description;
        !           116: }
        !           117:
        !           118: void describedItemBasedOnParameters(short theCategory, short theKind, short theQuantity, short theOriginDepth, char *buf) {
        !           119:     item *tempItem = initializeItem();
        !           120:     tempItem->category = theCategory;
        !           121:     tempItem->kind = theKind;
        !           122:     tempItem->quantity = theQuantity;
        !           123:     tempItem->originDepth = theOriginDepth;
        !           124:     itemName(tempItem, buf, false, true, NULL);
        !           125:     free(tempItem);
        !           126:     return;
        !           127: }
        !           128:
        !           129: // Describes the item in question either by naming it if the player has already seen its name,
        !           130: // or by tersely identifying its category otherwise.
        !           131: void describedItemName(item *theItem, char *buf) {
        !           132:     if (rogue.playbackOmniscience || (!player.status[STATUS_HALLUCINATING])) {
        !           133:         itemName(theItem, buf, (theItem->category & (WEAPON | ARMOR) ? false : true), true, NULL);
        !           134:     } else {
        !           135:         describeHallucinatedItem(buf);
        !           136:     }
        !           137: }
        !           138:
        !           139: void describeLocation(char *buf, short x, short y) {
        !           140:     creature *monst;
        !           141:     item *theItem, *magicItem;
        !           142:     boolean standsInTerrain;
        !           143:     boolean subjectMoving;
        !           144:     boolean prepositionLocked = false;
        !           145:     boolean monsterDormant;
        !           146:
        !           147:     char subject[COLS * 3];
        !           148:     char verb[COLS * 3];
        !           149:     char preposition[COLS * 3];
        !           150:     char object[COLS * 3];
        !           151:     char adjective[COLS * 3];
        !           152:
        !           153:     assureCosmeticRNG;
        !           154:
        !           155:     if (x == player.xLoc && y == player.yLoc) {
        !           156:         if (player.status[STATUS_LEVITATING]) {
        !           157:             sprintf(buf, "you are hovering above %s.", tileText(x, y));
        !           158:         } else {
        !           159:             strcpy(buf, tileFlavor(x, y));
        !           160:         }
        !           161:         restoreRNG;
        !           162:         return;
        !           163:     }
        !           164:
        !           165:     monst = NULL;
        !           166:     standsInTerrain = ((tileCatalog[pmap[x][y].layers[highestPriorityLayer(x, y, false)]].mechFlags & TM_STAND_IN_TILE) ? true : false);
        !           167:     theItem = itemAtLoc(x, y);
        !           168:     monsterDormant = false;
        !           169:     if (pmap[x][y].flags & HAS_MONSTER) {
        !           170:         monst = monsterAtLoc(x, y);
        !           171:     } else if (pmap[x][y].flags & HAS_DORMANT_MONSTER) {
        !           172:         monst = dormantMonsterAtLoc(x, y);
        !           173:         monsterDormant = true;
        !           174:     }
        !           175:
        !           176:     // detecting magical items
        !           177:     magicItem = NULL;
        !           178:     if (theItem && !playerCanSeeOrSense(x, y)
        !           179:         && (theItem->flags & ITEM_MAGIC_DETECTED)
        !           180:         && itemMagicPolarity(theItem)) {
        !           181:         magicItem = theItem;
        !           182:     } else if (monst && !canSeeMonster(monst)
        !           183:                && monst->carriedItem
        !           184:                && (monst->carriedItem->flags & ITEM_MAGIC_DETECTED)
        !           185:                && itemMagicPolarity(monst->carriedItem)) {
        !           186:         magicItem = monst->carriedItem;
        !           187:     }
        !           188:     if (magicItem && !(pmap[x][y].flags & DISCOVERED)) {
        !           189:         switch (itemMagicPolarity(magicItem)) {
        !           190:             case 1:
        !           191:                 strcpy(object, magicItem->category == AMULET ? "the Amulet of Yendor" : "benevolent magic");
        !           192:                 break;
        !           193:             case -1:
        !           194:                 strcpy(object, "malevolent magic");
        !           195:                 break;
        !           196:             default:
        !           197:                 strcpy(object, "mysterious magic");
        !           198:                 break;
        !           199:         }
        !           200:         sprintf(buf, "you can detect the aura of %s here.", object);
        !           201:         restoreRNG;
        !           202:         return;
        !           203:     }
        !           204:
        !           205:     // telepathy
        !           206:     if (monst
        !           207:         && !canSeeMonster(monst)
        !           208:         && monsterRevealed(monst)) {
        !           209:
        !           210:         strcpy(adjective, (((!player.status[STATUS_HALLUCINATING] || rogue.playbackOmniscience) && !monst->info.isLarge)
        !           211:                            || (player.status[STATUS_HALLUCINATING] && !rogue.playbackOmniscience && rand_range(0, 1)) ? "small" : "large"));
        !           212:         if (pmap[x][y].flags & DISCOVERED) {
        !           213:             strcpy(object, tileText(x, y));
        !           214:             if (monst->bookkeepingFlags & MB_SUBMERGED) {
        !           215:                 strcpy(preposition, "under ");
        !           216:             } else if (monsterDormant) {
        !           217:                 strcpy(preposition, "coming from within ");
        !           218:             } else if (standsInTerrain) {
        !           219:                 strcpy(preposition, "in ");
        !           220:             } else {
        !           221:                 strcpy(preposition, "over ");
        !           222:             }
        !           223:         } else {
        !           224:             strcpy(object, "here");
        !           225:             strcpy(preposition, "");
        !           226:         }
        !           227:
        !           228:         sprintf(buf, "you can sense a %s psychic emanation %s%s.", adjective, preposition, object);
        !           229:         restoreRNG;
        !           230:         return;
        !           231:     }
        !           232:
        !           233:     if (monst && !canSeeMonster(monst) && !rogue.playbackOmniscience) {
        !           234:         // Monster is not visible.
        !           235:         monst = NULL;
        !           236:     }
        !           237:
        !           238:     if (!playerCanSeeOrSense(x, y)) {
        !           239:         if (pmap[x][y].flags & DISCOVERED) { // memory
        !           240:             if (pmap[x][y].rememberedItemCategory) {
        !           241:                 if (player.status[STATUS_HALLUCINATING] && !rogue.playbackOmniscience) {
        !           242:                     describeHallucinatedItem(object);
        !           243:                 } else {
        !           244:                     describedItemBasedOnParameters(pmap[x][y].rememberedItemCategory, pmap[x][y].rememberedItemKind,
        !           245:                                                    pmap[x][y].rememberedItemQuantity, pmap[x][y].rememberedItemOriginDepth, object);
        !           246:                 }
        !           247:             } else {
        !           248:                 strcpy(object, tileCatalog[pmap[x][y].rememberedTerrain].description);
        !           249:             }
        !           250:             sprintf(buf, "you remember seeing %s here.", object);
        !           251:             restoreRNG;
        !           252:             return;
        !           253:         } else if (pmap[x][y].flags & MAGIC_MAPPED) { // magic mapped
        !           254:             sprintf(buf, "you expect %s to be here.", tileCatalog[pmap[x][y].rememberedTerrain].description);
        !           255:             restoreRNG;
        !           256:             return;
        !           257:         }
        !           258:         strcpy(buf, "");
        !           259:         restoreRNG;
        !           260:         return;
        !           261:     }
        !           262:
        !           263:     if (monst) {
        !           264:
        !           265:         monsterName(subject, monst, true);
        !           266:
        !           267:         if (pmap[x][y].layers[GAS] && monst->status[STATUS_INVISIBLE]) { // phantoms in gas
        !           268:             sprintf(buf, "you can perceive the faint outline of %s in %s.", subject, tileCatalog[pmap[x][y].layers[GAS]].description);
        !           269:             restoreRNG;
        !           270:             return;
        !           271:         }
        !           272:
        !           273:         subjectMoving = (monst->turnsSpentStationary == 0
        !           274:                          && !(monst->info.flags & (MONST_GETS_TURN_ON_ACTIVATION | MONST_IMMOBILE))
        !           275:                          && monst->creatureState != MONSTER_SLEEPING
        !           276:                          && !(monst->bookkeepingFlags & (MB_SEIZED | MB_CAPTIVE)));
        !           277:         if ((monst->info.flags & MONST_ATTACKABLE_THRU_WALLS)
        !           278:             && cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY)) {
        !           279:             strcpy(verb, "is embedded");
        !           280:         } else if (cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY)) {
        !           281:             strcpy(verb, "is trapped");
        !           282:             subjectMoving = false;
        !           283:         } else if (monst->bookkeepingFlags & MB_CAPTIVE) {
        !           284:             strcpy(verb, "is shackled in place");
        !           285:             subjectMoving = false;
        !           286:         } else if (monst->status[STATUS_PARALYZED]) {
        !           287:             strcpy(verb, "is frozen in place");
        !           288:             subjectMoving = false;
        !           289:         } else if (monst->status[STATUS_STUCK]) {
        !           290:             strcpy(verb, "is entangled");
        !           291:             subjectMoving = false;
        !           292:         } else if (monst->status[STATUS_LEVITATING]) {
        !           293:             strcpy(verb, (subjectMoving ? "is flying" : "is hovering"));
        !           294:             strcpy(preposition, "over");
        !           295:             prepositionLocked = true;
        !           296:         } else if (monsterCanSubmergeNow(monst)) {
        !           297:             strcpy(verb, (subjectMoving ? "is gliding" : "is drifting"));
        !           298:         } else if (cellHasTerrainFlag(x, y, T_MOVES_ITEMS) && !(monst->info.flags & MONST_SUBMERGES)) {
        !           299:             strcpy(verb, (subjectMoving ? "is swimming" : "is struggling"));
        !           300:         } else if (cellHasTerrainFlag(x, y, T_AUTO_DESCENT)) {
        !           301:             strcpy(verb, "is suspended in mid-air");
        !           302:             strcpy(preposition, "over");
        !           303:             prepositionLocked = true;
        !           304:             subjectMoving = false;
        !           305:         } else if (monst->status[STATUS_CONFUSED]) {
        !           306:             strcpy(verb, "is staggering");
        !           307:         } else if ((monst->info.flags & MONST_RESTRICTED_TO_LIQUID)
        !           308:                    && !cellHasTMFlag(monst->xLoc, monst->yLoc, TM_ALLOWS_SUBMERGING)) {
        !           309:             strcpy(verb, "is lying");
        !           310:             subjectMoving = false;
        !           311:         } else if (monst->info.flags & MONST_IMMOBILE) {
        !           312:             strcpy(verb, "is resting");
        !           313:         } else {
        !           314:             switch (monst->creatureState) {
        !           315:                 case MONSTER_SLEEPING:
        !           316:                     strcpy(verb, "is sleeping");
        !           317:                     subjectMoving = false;
        !           318:                     break;
        !           319:                 case MONSTER_WANDERING:
        !           320:                     strcpy(verb, subjectMoving ? "is wandering" : "is standing");
        !           321:                     break;
        !           322:                 case MONSTER_FLEEING:
        !           323:                     strcpy(verb, subjectMoving ? "is fleeing" : "is standing");
        !           324:                     break;
        !           325:                 case MONSTER_TRACKING_SCENT:
        !           326:                     strcpy(verb, subjectMoving ? "is charging" : "is standing");
        !           327:                     break;
        !           328:                 case MONSTER_ALLY:
        !           329:                     strcpy(verb, subjectMoving ? "is following you" : "is standing");
        !           330:                     break;
        !           331:                 default:
        !           332:                     strcpy(verb, "is standing");
        !           333:                     break;
        !           334:             }
        !           335:         }
        !           336:         if (monst->status[STATUS_BURNING] && !(monst->info.flags & MONST_FIERY)) {
        !           337:             strcat(verb, ", burning,");
        !           338:         }
        !           339:
        !           340:         if (theItem) {
        !           341:             strcpy(preposition, "over");
        !           342:             describedItemName(theItem, object);
        !           343:         } else {
        !           344:             if (!prepositionLocked) {
        !           345:                 strcpy(preposition, subjectMoving ? (standsInTerrain ? "through" : "across")
        !           346:                        : (standsInTerrain ? "in" : "on"));
        !           347:             }
        !           348:
        !           349:             strcpy(object, tileText(x, y));
        !           350:
        !           351:         }
        !           352:     } else { // no monster
        !           353:         strcpy(object, tileText(x, y));
        !           354:         if (theItem) {
        !           355:             describedItemName(theItem, subject);
        !           356:             subjectMoving = cellHasTerrainFlag(x, y, T_MOVES_ITEMS);
        !           357:             if (player.status[STATUS_HALLUCINATING] && !rogue.playbackOmniscience) {
        !           358:                 strcpy(verb, "is");
        !           359:             } else {
        !           360:                 strcpy(verb, (theItem->quantity > 1 || (theItem->category & GOLD)) ? "are" : "is");
        !           361:             }
        !           362:             if (cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY)) {
        !           363:                 strcat(verb, " enclosed");
        !           364:             } else {
        !           365:                 strcat(verb, subjectMoving ? " drifting" : " lying");
        !           366:             }
        !           367:             strcpy(preposition, standsInTerrain ? (subjectMoving ? "through" : "in")
        !           368:                    : (subjectMoving ? "across" : "on"));
        !           369:
        !           370:
        !           371:         } else { // no item
        !           372:             sprintf(buf, "you %s %s.", (playerCanDirectlySee(x, y) ? "see" : "sense"), object);
        !           373:             restoreRNG;
        !           374:             return;
        !           375:         }
        !           376:     }
        !           377:
        !           378:     sprintf(buf, "%s %s %s %s.", subject, verb, preposition, object);
        !           379:     restoreRNG;
        !           380: }
        !           381:
        !           382: void printLocationDescription(short x, short y) {
        !           383:     char buf[DCOLS*3];
        !           384:     describeLocation(buf, x, y);
        !           385:     flavorMessage(buf);
        !           386: }
        !           387:
        !           388: void useKeyAt(item *theItem, short x, short y) {
        !           389:     short layer, i;
        !           390:     creature *monst;
        !           391:     char buf[COLS], buf2[COLS], terrainName[COLS], preposition[10];
        !           392:     boolean disposable;
        !           393:
        !           394:     strcpy(terrainName, "unknown terrain"); // redundant failsafe
        !           395:     for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
        !           396:         if (tileCatalog[pmap[x][y].layers[layer]].mechFlags & TM_PROMOTES_WITH_KEY) {
        !           397:             if (tileCatalog[pmap[x][y].layers[layer]].description[0] == 'a'
        !           398:                 && tileCatalog[pmap[x][y].layers[layer]].description[1] == ' ') {
        !           399:                 sprintf(terrainName, "the %s", &(tileCatalog[pmap[x][y].layers[layer]].description[2]));
        !           400:             } else {
        !           401:                 strcpy(terrainName, tileCatalog[pmap[x][y].layers[layer]].description);
        !           402:             }
        !           403:             if (tileCatalog[pmap[x][y].layers[layer]].mechFlags & TM_STAND_IN_TILE) {
        !           404:                 strcpy(preposition, "in");
        !           405:             } else {
        !           406:                 strcpy(preposition, "on");
        !           407:             }
        !           408:             promoteTile(x, y, layer, false);
        !           409:         }
        !           410:     }
        !           411:
        !           412:     disposable = false;
        !           413:     for (i=0; i < KEY_ID_MAXIMUM && (theItem->keyLoc[i].x || theItem->keyLoc[i].machine); i++) {
        !           414:         if (theItem->keyLoc[i].x == x && theItem->keyLoc[i].y == y && theItem->keyLoc[i].disposableHere) {
        !           415:             disposable = true;
        !           416:         } else if (theItem->keyLoc[i].machine == pmap[x][y].machineNumber && theItem->keyLoc[i].disposableHere) {
        !           417:             disposable = true;
        !           418:         }
        !           419:     }
        !           420:
        !           421:     if (disposable) {
        !           422:         if (removeItemFromChain(theItem, packItems)) {
        !           423:             itemName(theItem, buf2, true, false, NULL);
        !           424:             sprintf(buf, "you use your %s %s %s.",
        !           425:                     buf2,
        !           426:                     preposition,
        !           427:                     terrainName);
        !           428:             messageWithColor(buf, &itemMessageColor, false);
        !           429:             deleteItem(theItem);
        !           430:         } else if (removeItemFromChain(theItem, floorItems)) {
        !           431:             deleteItem(theItem);
        !           432:             pmap[x][y].flags &= ~HAS_ITEM;
        !           433:         } else if (pmap[x][y].flags & HAS_MONSTER) {
        !           434:             monst = monsterAtLoc(x, y);
        !           435:             if (monst->carriedItem && monst->carriedItem == theItem) {
        !           436:                 monst->carriedItem = NULL;
        !           437:                 deleteItem(theItem);
        !           438:             }
        !           439:         }
        !           440:     }
        !           441: }
        !           442:
        !           443: short randValidDirectionFrom(creature *monst, short x, short y, boolean respectAvoidancePreferences) {
        !           444:     short i, newX, newY, validDirections[8], count = 0;
        !           445:
        !           446:     brogueAssert(rogue.RNG == RNG_SUBSTANTIVE);
        !           447:     for (i=0; i<8; i++) {
        !           448:         newX = x + nbDirs[i][0];
        !           449:         newY = y + nbDirs[i][1];
        !           450:         if (coordinatesAreInMap(newX, newY)
        !           451:             && !cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)
        !           452:             && !diagonalBlocked(x, y, newX, newY, false)
        !           453:             && (!respectAvoidancePreferences
        !           454:                 || (!monsterAvoids(monst, newX, newY))
        !           455:                 || ((pmap[newX][newY].flags & HAS_PLAYER) && monst->creatureState != MONSTER_ALLY))) {
        !           456:             validDirections[count++] = i;
        !           457:         }
        !           458:     }
        !           459:     if (count == 0) {
        !           460:         // Rare, and important in this case that the function returns BEFORE a random roll is made to avoid OOS.
        !           461:         return NO_DIRECTION;
        !           462:     }
        !           463:     return validDirections[rand_range(0, count - 1)];
        !           464: }
        !           465:
        !           466: void vomit(creature *monst) {
        !           467:     char buf[COLS], monstName[COLS];
        !           468:     spawnDungeonFeature(monst->xLoc, monst->yLoc, &dungeonFeatureCatalog[DF_VOMIT], true, false);
        !           469:
        !           470:     if (canDirectlySeeMonster(monst)
        !           471:         && !rogue.automationActive) {
        !           472:
        !           473:         monsterName(monstName, monst, true);
        !           474:         sprintf(buf, "%s vomit%s profusely", monstName, (monst == &player ? "" : "s"));
        !           475:         combatMessage(buf, NULL);
        !           476:     }
        !           477: }
        !           478:
        !           479: void moveEntrancedMonsters(enum directions dir) {
        !           480:     creature *monst, *nextMonst;
        !           481:
        !           482:     dir = oppositeDirection(dir);
        !           483:
        !           484:     if (rogue.patchVersion >= 3) {
        !           485:         for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
        !           486:             monst->bookkeepingFlags &= ~MB_HAS_ENTRANCED_MOVED;
        !           487:         }
        !           488:
        !           489:         for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
        !           490:             if (!(monst->bookkeepingFlags & MB_HAS_ENTRANCED_MOVED)
        !           491:                 && monst->status[STATUS_ENTRANCED]
        !           492:                 && !monst->status[STATUS_STUCK]
        !           493:                 && !monst->status[STATUS_PARALYZED]
        !           494:                 && !(monst->bookkeepingFlags & MB_CAPTIVE)) {
        !           495:
        !           496:                 moveMonster(monst, nbDirs[dir][0], nbDirs[dir][1]);
        !           497:                 monst->bookkeepingFlags |= MB_HAS_ENTRANCED_MOVED;
        !           498:                 monst = monsters; // loop through from the beginning to be safe
        !           499:             }
        !           500:         }
        !           501:
        !           502:     } else {
        !           503:         for (monst = monsters->nextCreature; monst != NULL; monst = nextMonst) {
        !           504:             nextMonst = monst->nextCreature;
        !           505:             if (monst->status[STATUS_ENTRANCED]
        !           506:                 && !monst->status[STATUS_STUCK]
        !           507:                 && !monst->status[STATUS_PARALYZED]
        !           508:                 && !(monst->bookkeepingFlags & MB_CAPTIVE)) {
        !           509:
        !           510:                 moveMonster(monst, nbDirs[dir][0], nbDirs[dir][1]);
        !           511:             }
        !           512:         }
        !           513:     }
        !           514:
        !           515: }
        !           516:
        !           517: void becomeAllyWith(creature *monst) {
        !           518:     demoteMonsterFromLeadership(monst);
        !           519:     // Drop your item.
        !           520:     if (monst->carriedItem) {
        !           521:         makeMonsterDropItem(monst);
        !           522:     }
        !           523:     // If you're going to change into something, it should be friendly.
        !           524:     if (monst->carriedMonster) {
        !           525:         becomeAllyWith(monst->carriedMonster);
        !           526:     }
        !           527:     monst->creatureState = MONSTER_ALLY;
        !           528:     monst->bookkeepingFlags |= MB_FOLLOWER;
        !           529:     monst->leader = &player;
        !           530:     monst->bookkeepingFlags &= ~(MB_CAPTIVE | MB_SEIZED);
        !           531:     refreshDungeonCell(monst->xLoc, monst->yLoc);
        !           532: }
        !           533:
        !           534: void freeCaptive(creature *monst) {
        !           535:     char buf[COLS * 3], monstName[COLS];
        !           536:
        !           537:     becomeAllyWith(monst);
        !           538:     monsterName(monstName, monst, false);
        !           539:     sprintf(buf, "you free the grateful %s and gain a faithful ally.", monstName);
        !           540:     message(buf, false);
        !           541: }
        !           542:
        !           543: boolean freeCaptivesEmbeddedAt(short x, short y) {
        !           544:     creature *monst;
        !           545:
        !           546:     if (pmap[x][y].flags & HAS_MONSTER) {
        !           547:         // Free any captives trapped in the tunnelized terrain.
        !           548:         monst = monsterAtLoc(x, y);
        !           549:         if ((monst->bookkeepingFlags & MB_CAPTIVE)
        !           550:             && !(monst->info.flags & MONST_ATTACKABLE_THRU_WALLS)
        !           551:             && (cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY))) {
        !           552:             freeCaptive(monst);
        !           553:             return true;
        !           554:         }
        !           555:     }
        !           556:     return false;
        !           557: }
        !           558:
        !           559: // Do we need confirmation so we don't accidently hit an acid mound?
        !           560: boolean abortAttackAgainstAcidicTarget(creature *hitList[8]) {
        !           561:     short i;
        !           562:     char monstName[COLS], weaponName[COLS];
        !           563:     char buf[COLS*3];
        !           564:
        !           565:     if (rogue.weapon
        !           566:         && !(rogue.weapon->flags & ITEM_PROTECTED)
        !           567:         && !player.status[STATUS_HALLUCINATING]
        !           568:         && !player.status[STATUS_CONFUSED]) {
        !           569:
        !           570:         for (i=0; i<8; i++) {
        !           571:             if (hitList[i]
        !           572:                 && (hitList[i]->info.flags & MONST_DEFEND_DEGRADE_WEAPON)
        !           573:                 && canSeeMonster(hitList[i])
        !           574:                 && (!(rogue.weapon->flags & ITEM_RUNIC)
        !           575:                     || !(rogue.weapon->flags & ITEM_RUNIC_IDENTIFIED)
        !           576:                     || rogue.weapon->enchant2 != W_SLAYING
        !           577:                     || !monsterIsInClass(hitList[i], rogue.weapon->vorpalEnemy))) {
        !           578:
        !           579:                 monsterName(monstName, hitList[i], true);
        !           580:                 itemName(rogue.weapon, weaponName, false, false, NULL);
        !           581:                 sprintf(buf, "Degrade your %s by attacking %s?", weaponName, monstName);
        !           582:                 if (confirm(buf, false)) {
        !           583:                     return false; // Fire when ready!
        !           584:                 } else {
        !           585:                     return true; // Abort!
        !           586:                 }
        !           587:             }
        !           588:         }
        !           589:     }
        !           590:     return false;
        !           591: }
        !           592:
        !           593: // Returns true if a whip attack was launched.
        !           594: // If "aborted" pointer is provided, sets it to true if it was aborted because
        !           595: // the player opted not to attack an acid mound (in which case the whole turn
        !           596: // should be aborted), as opposed to there being no valid whip attack available
        !           597: // (in which case the player/monster should move instead).
        !           598: boolean handleWhipAttacks(creature *attacker, enum directions dir, boolean *aborted) {
        !           599:     bolt theBolt;
        !           600:     creature *defender, *hitList[8] = {0};
        !           601:     short strikeLoc[2], originLoc[2], targetLoc[2];
        !           602:
        !           603:     const char boltChar[DIRECTION_COUNT] = "||~~\\//\\";
        !           604:
        !           605:     brogueAssert(dir > NO_DIRECTION && dir < DIRECTION_COUNT);
        !           606:
        !           607:     if (attacker == &player) {
        !           608:         if (!rogue.weapon || !(rogue.weapon->flags & ITEM_ATTACKS_EXTEND)) {
        !           609:             return false;
        !           610:         }
        !           611:     } else if (!(attacker->info.abilityFlags & MA_ATTACKS_EXTEND)) {
        !           612:         return false;
        !           613:     }
        !           614:     originLoc[0] = attacker->xLoc;
        !           615:     originLoc[1] = attacker->yLoc;
        !           616:     targetLoc[0] = attacker->xLoc + nbDirs[dir][0];
        !           617:     targetLoc[1] = attacker->yLoc + nbDirs[dir][1];
        !           618:     getImpactLoc(strikeLoc, originLoc, targetLoc, 5, false);
        !           619:
        !           620:     defender = monsterAtLoc(strikeLoc[0], strikeLoc[1]);
        !           621:     if (defender
        !           622:         && (attacker != &player || canSeeMonster(defender))
        !           623:         && !monsterIsHidden(defender, attacker)
        !           624:         && monsterWillAttackTarget(attacker, defender)) {
        !           625:
        !           626:         if (attacker == &player) {
        !           627:             hitList[0] = defender;
        !           628:             if (abortAttackAgainstAcidicTarget(hitList)) {
        !           629:                 if (aborted) {
        !           630:                     *aborted = true;
        !           631:                 }
        !           632:                 return false;
        !           633:             }
        !           634:         }
        !           635:         attacker->bookkeepingFlags &= ~MB_SUBMERGED;
        !           636:         theBolt = boltCatalog[BOLT_WHIP];
        !           637:         theBolt.theChar = boltChar[dir];
        !           638:         zap(originLoc, targetLoc, &theBolt, false);
        !           639:         return true;
        !           640:     }
        !           641:     return false;
        !           642: }
        !           643:
        !           644: // Returns true if a spear attack was launched.
        !           645: // If "aborted" pointer is provided, sets it to true if it was aborted because
        !           646: // the player opted not to attack an acid mound (in which case the whole turn
        !           647: // should be aborted), as opposed to there being no valid spear attack available
        !           648: // (in which case the player/monster should move instead).
        !           649: boolean handleSpearAttacks(creature *attacker, enum directions dir, boolean *aborted) {
        !           650:     creature *defender, *hitList[8] = {0};
        !           651:     short targetLoc[2], range = 2, i = 0, h = 0;
        !           652:     boolean proceed = false, visualEffect = false;
        !           653:
        !           654:     const char boltChar[DIRECTION_COUNT] = "||--\\//\\";
        !           655:
        !           656:     brogueAssert(dir > NO_DIRECTION && dir < DIRECTION_COUNT);
        !           657:
        !           658:     if (attacker == &player) {
        !           659:         if (!rogue.weapon || !(rogue.weapon->flags & ITEM_ATTACKS_PENETRATE)) {
        !           660:             return false;
        !           661:         }
        !           662:     } else if (!(attacker->info.abilityFlags & MA_ATTACKS_PENETRATE)) {
        !           663:         return false;
        !           664:     }
        !           665:
        !           666:     for (i = 0; i < range; i++) {
        !           667:         targetLoc[0] = attacker->xLoc + (1 + i) * nbDirs[dir][0];
        !           668:         targetLoc[1] = attacker->yLoc + (1 + i) * nbDirs[dir][1];
        !           669:         if (!coordinatesAreInMap(targetLoc[0], targetLoc[1])) {
        !           670:             break;
        !           671:         }
        !           672:
        !           673:         /* Add creatures that we are willing to attack to the potential
        !           674:         hitlist. Any of those that are either right by us or visible will
        !           675:         trigger the attack. */
        !           676:         defender = monsterAtLoc(targetLoc[0], targetLoc[1]);
        !           677:         if (defender
        !           678:             && (!cellHasTerrainFlag(targetLoc[0], targetLoc[1], T_OBSTRUCTS_PASSABILITY)
        !           679:                 || (defender->info.flags & MONST_ATTACKABLE_THRU_WALLS))
        !           680:             && monsterWillAttackTarget(attacker, defender)) {
        !           681:
        !           682:             hitList[h++] = defender;
        !           683:
        !           684:             /* We check if i=0, i.e. the defender is right next to us, because
        !           685:             we have to do "normal" attacking here. We can't just return
        !           686:             false and leave to playerMoves/moveMonster due to the collateral hitlist. */
        !           687:             if (i == 0 || !monsterIsHidden(defender, attacker)
        !           688:                 && (attacker != &player || canSeeMonster(defender))) {
        !           689:                 // We'll attack.
        !           690:                 proceed = true;
        !           691:             }
        !           692:         }
        !           693:
        !           694:         if (cellHasTerrainFlag(targetLoc[0], targetLoc[1], (T_OBSTRUCTS_PASSABILITY | T_OBSTRUCTS_VISION))) {
        !           695:             break;
        !           696:         }
        !           697:     }
        !           698:     range = i;
        !           699:     if (proceed) {
        !           700:         if (attacker == &player) {
        !           701:             if (abortAttackAgainstAcidicTarget(hitList)) {
        !           702:                 if (aborted) {
        !           703:                     *aborted = true;
        !           704:                 }
        !           705:                 return false;
        !           706:             }
        !           707:         }
        !           708:         if (!rogue.playbackFastForward) {
        !           709:             for (i = 0; i < range; i++) {
        !           710:                 targetLoc[0] = attacker->xLoc + (1 + i) * nbDirs[dir][0];
        !           711:                 targetLoc[1] = attacker->yLoc + (1 + i) * nbDirs[dir][1];
        !           712:                 if (coordinatesAreInMap(targetLoc[0], targetLoc[1])
        !           713:                     && playerCanSeeOrSense(targetLoc[0], targetLoc[1])) {
        !           714:
        !           715:                     visualEffect = true;
        !           716:                     plotForegroundChar(boltChar[dir], targetLoc[0], targetLoc[1], &lightBlue, true);
        !           717:                 }
        !           718:             }
        !           719:         }
        !           720:         attacker->bookkeepingFlags &= ~MB_SUBMERGED;
        !           721:         // Artificially reverse the order of the attacks,
        !           722:         // so that spears of force can send both monsters flying.
        !           723:         for (i = h - 1; i >= 0; i--) {
        !           724:             attack(attacker, hitList[i], false);
        !           725:         }
        !           726:         if (visualEffect) {
        !           727:             pauseBrogue(16);
        !           728:             for (i = 0; i < range; i++) {
        !           729:                 targetLoc[0] = attacker->xLoc + (1 + i) * nbDirs[dir][0];
        !           730:                 targetLoc[1] = attacker->yLoc + (1 + i) * nbDirs[dir][1];
        !           731:                 if (coordinatesAreInMap(targetLoc[0], targetLoc[1])) {
        !           732:                     refreshDungeonCell(targetLoc[0], targetLoc[1]);
        !           733:                 }
        !           734:             }
        !           735:         }
        !           736:         return true;
        !           737:     }
        !           738:     return false;
        !           739: }
        !           740:
        !           741: void buildFlailHitList(const short x, const short y, const short newX, const short newY, creature *hitList[16]) {
        !           742:     creature *monst;
        !           743:     short mx, my;
        !           744:     short i = 0;
        !           745:
        !           746:     for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
        !           747:         mx = monst->xLoc;
        !           748:         my = monst->yLoc;
        !           749:         if (distanceBetween(x, y, mx, my) == 1
        !           750:             && distanceBetween(newX, newY, mx, my) == 1
        !           751:             && canSeeMonster(monst)
        !           752:             && monstersAreEnemies(&player, monst)
        !           753:             && monst->creatureState != MONSTER_ALLY
        !           754:             && !(monst->bookkeepingFlags & MB_IS_DYING)
        !           755:             && (!cellHasTerrainFlag(monst->xLoc, monst->yLoc, T_OBSTRUCTS_PASSABILITY) || (monst->info.flags & MONST_ATTACKABLE_THRU_WALLS))) {
        !           756:
        !           757:             while (hitList[i]) {
        !           758:                 i++;
        !           759:             }
        !           760:             hitList[i] = monst;
        !           761:         }
        !           762:     }
        !           763: }
        !           764:
        !           765: boolean diagonalBlocked(const short x1, const short y1, const short x2, const short y2, const boolean limitToPlayerKnowledge) {
        !           766:     unsigned long tFlags;
        !           767:     if (x1 == x2 || y1 == y2) {
        !           768:         return false; // If it's not a diagonal, it's not diagonally blocked.
        !           769:     }
        !           770:     getLocationFlags(x1, y2, &tFlags, NULL, NULL, limitToPlayerKnowledge);
        !           771:     if (tFlags & T_OBSTRUCTS_DIAGONAL_MOVEMENT) {
        !           772:         return true;
        !           773:     }
        !           774:     getLocationFlags(x2, y1, &tFlags, NULL, NULL, limitToPlayerKnowledge);
        !           775:     if (tFlags & T_OBSTRUCTS_DIAGONAL_MOVEMENT) {
        !           776:         return true;
        !           777:     }
        !           778:     return false;
        !           779: }
        !           780:
        !           781: // Called whenever the player voluntarily tries to move in a given direction.
        !           782: // Can be called from movement keys, exploration, or auto-travel.
        !           783: boolean playerMoves(short direction) {
        !           784:     short initialDirection = direction, i, layer;
        !           785:     short x = player.xLoc, y = player.yLoc;
        !           786:     short newX, newY, newestX, newestY;
        !           787:     boolean playerMoved = false, alreadyRecorded = false, specialAttackAborted = false, anyAttackHit = false;
        !           788:     creature *defender = NULL, *tempMonst = NULL, *hitList[16] = {NULL};
        !           789:     char monstName[COLS];
        !           790:     char buf[COLS*3];
        !           791:     const int directionKeys[8] = {UP_KEY, DOWN_KEY, LEFT_KEY, RIGHT_KEY, UPLEFT_KEY, DOWNLEFT_KEY, UPRIGHT_KEY, DOWNRIGHT_KEY};
        !           792:
        !           793:     brogueAssert(direction >= 0 && direction < DIRECTION_COUNT);
        !           794:
        !           795:     newX = x + nbDirs[direction][0];
        !           796:     newY = y + nbDirs[direction][1];
        !           797:
        !           798:     if (!coordinatesAreInMap(newX, newY)) {
        !           799:         return false;
        !           800:     }
        !           801:
        !           802:     if (player.status[STATUS_CONFUSED]) {
        !           803:         // Confirmation dialog if you're moving while confused and you're next to lava and not levitating or immune to fire.
        !           804:         if (player.status[STATUS_LEVITATING] <= 1
        !           805:             && player.status[STATUS_IMMUNE_TO_FIRE] <= 1) {
        !           806:
        !           807:             for (i=0; i<8; i++) {
        !           808:                 newestX = x + nbDirs[i][0];
        !           809:                 newestY = y + nbDirs[i][1];
        !           810:                 if (coordinatesAreInMap(newestX, newestY)
        !           811:                     && (pmap[newestX][newestY].flags & (DISCOVERED | MAGIC_MAPPED))
        !           812:                     && !diagonalBlocked(x, y, newestX, newestY, false)
        !           813:                     && cellHasTerrainFlag(newestX, newestY, T_LAVA_INSTA_DEATH)
        !           814:                     && !cellHasTerrainFlag(newestX, newestY, T_OBSTRUCTS_PASSABILITY | T_ENTANGLES)
        !           815:                     && !((pmap[newestX][newestY].flags & HAS_MONSTER)
        !           816:                          && canSeeMonster(monsterAtLoc(newestX, newestY))
        !           817:                          && monsterAtLoc(newestX, newestY)->creatureState != MONSTER_ALLY)) {
        !           818:
        !           819:                     if (!confirm("Risk stumbling into lava?", false)) {
        !           820:                         return false;
        !           821:                     } else {
        !           822:                         break;
        !           823:                     }
        !           824:                 }
        !           825:             }
        !           826:         }
        !           827:
        !           828:         direction = randValidDirectionFrom(&player, x, y, false);
        !           829:         if (direction == -1) {
        !           830:             return false;
        !           831:         } else {
        !           832:             newX = x + nbDirs[direction][0];
        !           833:             newY = y + nbDirs[direction][1];
        !           834:             if (!coordinatesAreInMap(newX, newY)) {
        !           835:                 return false;
        !           836:             }
        !           837:             if (!alreadyRecorded) {
        !           838:                 recordKeystroke(directionKeys[initialDirection], false, false);
        !           839:                 alreadyRecorded = true;
        !           840:             }
        !           841:         }
        !           842:     }
        !           843:
        !           844:     if (pmap[newX][newY].flags & HAS_MONSTER) {
        !           845:         defender = monsterAtLoc(newX, newY);
        !           846:     }
        !           847:
        !           848:     // If there's no enemy at the movement location that the player is aware of, consider terrain promotions.
        !           849:     if (!defender
        !           850:         || (!canSeeMonster(defender) && !monsterRevealed(defender))
        !           851:         || !monstersAreEnemies(&player, defender)) {
        !           852:
        !           853:         if (cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY) && cellHasTMFlag(newX, newY, TM_PROMOTES_ON_PLAYER_ENTRY)) {
        !           854:             layer = layerWithTMFlag(newX, newY, TM_PROMOTES_ON_PLAYER_ENTRY);
        !           855:             if (tileCatalog[pmap[newX][newY].layers[layer]].flags & T_OBSTRUCTS_PASSABILITY) {
        !           856:                 if (!alreadyRecorded) {
        !           857:                     recordKeystroke(directionKeys[initialDirection], false, false);
        !           858:                     alreadyRecorded = true;
        !           859:                 }
        !           860:                 message(tileCatalog[pmap[newX][newY].layers[layer]].flavorText, false);
        !           861:                 promoteTile(newX, newY, layer, false);
        !           862:                 playerTurnEnded();
        !           863:                 return true;
        !           864:             }
        !           865:         }
        !           866:
        !           867:         if (rogue.patchVersion < 1 && player.status[STATUS_STUCK] && cellHasTerrainFlag(x, y, T_ENTANGLES)) {
        !           868:                 // Don't interrupt exploration with this message.
        !           869:             if (--player.status[STATUS_STUCK]) {
        !           870:                 if (!rogue.automationActive) {
        !           871:                     message("you struggle but cannot free yourself.", false);
        !           872:                 }
        !           873:             } else {
        !           874:                 if (!rogue.automationActive) {
        !           875:                     message("you break free!", false);
        !           876:                 }
        !           877:                 if (tileCatalog[pmap[x][y].layers[SURFACE]].flags & T_ENTANGLES) {
        !           878:                     pmap[x][y].layers[SURFACE] = NOTHING;
        !           879:                 }
        !           880:             }
        !           881:             moveEntrancedMonsters(direction);
        !           882:             if (!alreadyRecorded) {
        !           883:                 recordKeystroke(directionKeys[initialDirection], false, false);
        !           884:                 alreadyRecorded = true;
        !           885:             }
        !           886:             if (player.status[STATUS_STUCK]) {
        !           887:                 playerTurnEnded();
        !           888:                 return true;
        !           889:             }
        !           890:         }
        !           891:     }
        !           892:
        !           893:     if (((!cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY) || (cellHasTMFlag(newX, newY, TM_PROMOTES_WITH_KEY) && keyInPackFor(newX, newY)))
        !           894:          && !diagonalBlocked(x, y, newX, newY, false)
        !           895:          && (!cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY) || (cellHasTMFlag(x, y, TM_PROMOTES_WITH_KEY) && keyInPackFor(x, y))))
        !           896:         || (defender && defender->info.flags & MONST_ATTACKABLE_THRU_WALLS)) {
        !           897:         // if the move is not blocked
        !           898:
        !           899:         if (handleWhipAttacks(&player, direction, &specialAttackAborted)
        !           900:             || handleSpearAttacks(&player, direction, &specialAttackAborted)) {
        !           901:
        !           902:             if (!alreadyRecorded) {
        !           903:                 recordKeystroke(directionKeys[initialDirection], false, false);
        !           904:                 alreadyRecorded = true;
        !           905:             }
        !           906:             playerRecoversFromAttacking(true);
        !           907:             moveEntrancedMonsters(direction);
        !           908:             playerTurnEnded();
        !           909:             return true;
        !           910:         } else if (specialAttackAborted) { // Canceled an attack against an acid mound.
        !           911:             brogueAssert(!alreadyRecorded);
        !           912:             rogue.disturbed = true;
        !           913:             return false;
        !           914:         }
        !           915:
        !           916:         if (defender) {
        !           917:             // if there is a monster there
        !           918:
        !           919:             if (defender->bookkeepingFlags & MB_CAPTIVE) {
        !           920:                 monsterName(monstName, defender, false);
        !           921:                 sprintf(buf, "Free the captive %s?", monstName);
        !           922:                 if (alreadyRecorded || confirm(buf, false)) {
        !           923:                     if (!alreadyRecorded) {
        !           924:                         recordKeystroke(directionKeys[initialDirection], false, false);
        !           925:                         alreadyRecorded = true;
        !           926:                     }
        !           927:                     if (cellHasTMFlag(newX, newY, TM_PROMOTES_WITH_KEY) && keyInPackFor(newX, newY)) {
        !           928:                         useKeyAt(keyInPackFor(newX, newY), newX, newY);
        !           929:                     }
        !           930:                     freeCaptive(defender);
        !           931:                     player.ticksUntilTurn += player.attackSpeed;
        !           932:                     playerTurnEnded();
        !           933:                     return true;
        !           934:                 } else {
        !           935:                     return false;
        !           936:                 }
        !           937:             }
        !           938:
        !           939:             if (defender->creatureState != MONSTER_ALLY) {
        !           940:                 // Make a hit list of monsters the player is attacking this turn.
        !           941:                 // We separate this tallying phase from the actual attacking phase because sometimes the attacks themselves
        !           942:                 // create more monsters, and those shouldn't be attacked in the same turn.
        !           943:
        !           944:                 buildHitList(hitList, &player, defender,
        !           945:                              rogue.weapon && (rogue.weapon->flags & ITEM_ATTACKS_ALL_ADJACENT));
        !           946:
        !           947:                 if (abortAttackAgainstAcidicTarget(hitList)) { // Acid mound attack confirmation.
        !           948:                     brogueAssert(!alreadyRecorded);
        !           949:                     rogue.disturbed = true;
        !           950:                     return false;
        !           951:                 }
        !           952:
        !           953:                 if (player.status[STATUS_NAUSEOUS]) {
        !           954:                     if (!alreadyRecorded) {
        !           955:                         recordKeystroke(directionKeys[initialDirection], false, false);
        !           956:                         alreadyRecorded = true;
        !           957:                     }
        !           958:                     if (rand_percent(25)) {
        !           959:                         vomit(&player);
        !           960:                         playerTurnEnded();
        !           961:                         return false;
        !           962:                     }
        !           963:                 }
        !           964:
        !           965:                 // Proceeding with the attack.
        !           966:
        !           967:                 if (!alreadyRecorded) {
        !           968:                     recordKeystroke(directionKeys[initialDirection], false, false);
        !           969:                     alreadyRecorded = true;
        !           970:                 }
        !           971:
        !           972:                 // Attack!
        !           973:                 for (i=0; i<16; i++) {
        !           974:                     if (hitList[i]
        !           975:                         && monsterWillAttackTarget(&player, hitList[i])
        !           976:                         && !(hitList[i]->bookkeepingFlags & MB_IS_DYING)
        !           977:                         && !rogue.gameHasEnded) {
        !           978:
        !           979:                         if (attack(&player, hitList[i], false)) {
        !           980:                             anyAttackHit = true;
        !           981:                         }
        !           982:                     }
        !           983:                 }
        !           984:
        !           985:                 playerRecoversFromAttacking(anyAttackHit);
        !           986:                 moveEntrancedMonsters(direction);
        !           987:                 playerTurnEnded();
        !           988:                 return true;
        !           989:             }
        !           990:         }
        !           991:
        !           992:         if (player.bookkeepingFlags & MB_SEIZED) {
        !           993:             for (tempMonst = monsters->nextCreature; tempMonst != NULL; tempMonst = tempMonst->nextCreature) {
        !           994:                 if ((tempMonst->bookkeepingFlags & MB_SEIZING)
        !           995:                     && monstersAreEnemies(&player, tempMonst)
        !           996:                     && distanceBetween(player.xLoc, player.yLoc, tempMonst->xLoc, tempMonst->yLoc) == 1
        !           997:                     && !diagonalBlocked(player.xLoc, player.yLoc, tempMonst->xLoc, tempMonst->yLoc, false)
        !           998:                     && !tempMonst->status[STATUS_ENTRANCED]) {
        !           999:
        !          1000:                     monsterName(monstName, tempMonst, true);
        !          1001:                     if (alreadyRecorded || !canSeeMonster(tempMonst)) {
        !          1002:                         if (!alreadyRecorded) {
        !          1003:                             recordKeystroke(directionKeys[initialDirection], false, false);
        !          1004:                             alreadyRecorded = true;
        !          1005:                         }
        !          1006:                         sprintf(buf, "you struggle but %s is holding your legs!", monstName);
        !          1007:                         moveEntrancedMonsters(direction);
        !          1008:                         message(buf, false);
        !          1009:                         playerTurnEnded();
        !          1010:                         return true;
        !          1011:                     } else {
        !          1012:                         sprintf(buf, "you cannot move; %s is holding your legs!", monstName);
        !          1013:                         message(buf, false);
        !          1014:                         return false;
        !          1015:                     }
        !          1016:                 }
        !          1017:             }
        !          1018:             player.bookkeepingFlags &= ~MB_SEIZED; // failsafe
        !          1019:         }
        !          1020:
        !          1021:         if (pmap[newX][newY].flags & (DISCOVERED | MAGIC_MAPPED)
        !          1022:             && player.status[STATUS_LEVITATING] <= 1
        !          1023:             && !player.status[STATUS_CONFUSED]
        !          1024:             && cellHasTerrainFlag(newX, newY, T_LAVA_INSTA_DEATH)
        !          1025:             && player.status[STATUS_IMMUNE_TO_FIRE] <= 1
        !          1026:             && !cellHasTerrainFlag(newX, newY, T_ENTANGLES)
        !          1027:             && !cellHasTMFlag(newX, newY, TM_IS_SECRET)) {
        !          1028:             message("that would be certain death!", false);
        !          1029:             return false; // player won't willingly step into lava
        !          1030:         } else if (pmap[newX][newY].flags & (DISCOVERED | MAGIC_MAPPED)
        !          1031:                    && player.status[STATUS_LEVITATING] <= 1
        !          1032:                    && !player.status[STATUS_CONFUSED]
        !          1033:                    && cellHasTerrainFlag(newX, newY, T_AUTO_DESCENT)
        !          1034:                    && !cellHasTerrainFlag(newX, newY, T_ENTANGLES)
        !          1035:                    && !cellHasTMFlag(newX, newY, TM_IS_SECRET)
        !          1036:                    && !confirm("Dive into the depths?", false)) {
        !          1037:             return false;
        !          1038:         } else if (playerCanSee(newX, newY)
        !          1039:                    && !player.status[STATUS_CONFUSED]
        !          1040:                    && !player.status[STATUS_BURNING]
        !          1041:                    && player.status[STATUS_IMMUNE_TO_FIRE] <= 1
        !          1042:                    && cellHasTerrainFlag(newX, newY, T_IS_FIRE)
        !          1043:                    && !cellHasTMFlag(newX, newY, TM_EXTINGUISHES_FIRE)
        !          1044:                    && !confirm("Venture into flame?", false)) {
        !          1045:             return false;
        !          1046:         } else if (playerCanSee(newX, newY)
        !          1047:                    && !player.status[STATUS_CONFUSED]
        !          1048:                    && !player.status[STATUS_BURNING]
        !          1049:                    && cellHasTerrainFlag(newX, newY, T_CAUSES_CONFUSION | T_CAUSES_PARALYSIS)
        !          1050:                    && (!rogue.armor || !(rogue.armor->flags & ITEM_RUNIC) || !(rogue.armor->flags & ITEM_RUNIC_IDENTIFIED) || rogue.armor->enchant2 != A_RESPIRATION)
        !          1051:                    && !confirm("Venture into dangerous gas?", false)) {
        !          1052:             return false;
        !          1053:         } else if (pmap[newX][newY].flags & (ANY_KIND_OF_VISIBLE | MAGIC_MAPPED)
        !          1054:                    && player.status[STATUS_LEVITATING] <= 1
        !          1055:                    && !player.status[STATUS_CONFUSED]
        !          1056:                    && cellHasTerrainFlag(newX, newY, T_IS_DF_TRAP)
        !          1057:                    && !(pmap[newX][newY].flags & PRESSURE_PLATE_DEPRESSED)
        !          1058:                    && !cellHasTMFlag(newX, newY, TM_IS_SECRET)
        !          1059:                    && !confirm("Step onto the pressure plate?", false)) {
        !          1060:             return false;
        !          1061:         }
        !          1062:
        !          1063:         if (rogue.weapon && (rogue.weapon->flags & ITEM_LUNGE_ATTACKS)) {
        !          1064:             newestX = player.xLoc + 2*nbDirs[direction][0];
        !          1065:             newestY = player.yLoc + 2*nbDirs[direction][1];
        !          1066:             if (coordinatesAreInMap(newestX, newestY) && (pmap[newestX][newestY].flags & HAS_MONSTER)) {
        !          1067:                 tempMonst = monsterAtLoc(newestX, newestY);
        !          1068:                 if (tempMonst
        !          1069:                     && canSeeMonster(tempMonst)
        !          1070:                     && monstersAreEnemies(&player, tempMonst)
        !          1071:                     && tempMonst->creatureState != MONSTER_ALLY
        !          1072:                     && !(tempMonst->bookkeepingFlags & MB_IS_DYING)
        !          1073:                     && (!cellHasTerrainFlag(tempMonst->xLoc, tempMonst->yLoc, T_OBSTRUCTS_PASSABILITY) || (tempMonst->info.flags & MONST_ATTACKABLE_THRU_WALLS))) {
        !          1074:
        !          1075:                     hitList[0] = tempMonst;
        !          1076:                     if (abortAttackAgainstAcidicTarget(hitList)) { // Acid mound attack confirmation.
        !          1077:                         brogueAssert(!alreadyRecorded);
        !          1078:                         rogue.disturbed = true;
        !          1079:                         return false;
        !          1080:                     }
        !          1081:                 }
        !          1082:             }
        !          1083:         }
        !          1084:         if (rogue.weapon && (rogue.weapon->flags & ITEM_PASS_ATTACKS)) {
        !          1085:             buildFlailHitList(x, y, newX, newY, hitList);
        !          1086:             if (abortAttackAgainstAcidicTarget(hitList)) { // Acid mound attack confirmation.
        !          1087:                 brogueAssert(!alreadyRecorded);
        !          1088:                 rogue.disturbed = true;
        !          1089:                 return false;
        !          1090:             }
        !          1091:         }
        !          1092:
        !          1093:         if (rogue.patchVersion >= 1 && player.status[STATUS_STUCK] && cellHasTerrainFlag(x, y, T_ENTANGLES)) {
        !          1094:                 // Don't interrupt exploration with this message.
        !          1095:             if (--player.status[STATUS_STUCK]) {
        !          1096:                 if (!rogue.automationActive) {
        !          1097:                     message("you struggle but cannot free yourself.", false);
        !          1098:                 }
        !          1099:                 moveEntrancedMonsters(direction);
        !          1100:                 if (!alreadyRecorded) {
        !          1101:                     recordKeystroke(directionKeys[initialDirection], false, false);
        !          1102:                     alreadyRecorded = true;
        !          1103:                 }
        !          1104:                 playerTurnEnded();
        !          1105:                 return true;
        !          1106:             } else {
        !          1107:                 if (!rogue.automationActive) {
        !          1108:                     message("you break free!", false);
        !          1109:                 }
        !          1110:                 if (tileCatalog[pmap[x][y].layers[SURFACE]].flags & T_ENTANGLES) {
        !          1111:                     pmap[x][y].layers[SURFACE] = NOTHING;
        !          1112:                 }
        !          1113:             }
        !          1114:         }
        !          1115:
        !          1116:         if (player.status[STATUS_NAUSEOUS]) {
        !          1117:             if (!alreadyRecorded) {
        !          1118:                 recordKeystroke(directionKeys[initialDirection], false, false);
        !          1119:                 alreadyRecorded = true;
        !          1120:             }
        !          1121:             if (rand_percent(25)) {
        !          1122:                 vomit(&player);
        !          1123:                 playerTurnEnded();
        !          1124:                 return true;
        !          1125:             }
        !          1126:         }
        !          1127:
        !          1128:         // Are we taking the stairs?
        !          1129:         if (rogue.downLoc[0] == newX && rogue.downLoc[1] == newY) {
        !          1130:             if (!alreadyRecorded) {
        !          1131:                 recordKeystroke(directionKeys[initialDirection], false, false);
        !          1132:                 alreadyRecorded = true;
        !          1133:             }
        !          1134:             useStairs(1);
        !          1135:         } else if (rogue.upLoc[0] == newX && rogue.upLoc[1] == newY) {
        !          1136:             if (!alreadyRecorded) {
        !          1137:                 recordKeystroke(directionKeys[initialDirection], false, false);
        !          1138:                 alreadyRecorded = true;
        !          1139:             }
        !          1140:             useStairs(-1);
        !          1141:         } else {
        !          1142:             // Okay, we're finally moving!
        !          1143:             if (!alreadyRecorded) {
        !          1144:                 recordKeystroke(directionKeys[initialDirection], false, false);
        !          1145:                 alreadyRecorded = true;
        !          1146:             }
        !          1147:
        !          1148:             player.xLoc += nbDirs[direction][0];
        !          1149:             player.yLoc += nbDirs[direction][1];
        !          1150:             pmap[x][y].flags &= ~HAS_PLAYER;
        !          1151:             pmap[player.xLoc][player.yLoc].flags |= HAS_PLAYER;
        !          1152:             pmap[player.xLoc][player.yLoc].flags &= ~IS_IN_PATH;
        !          1153:             if (defender && defender->creatureState == MONSTER_ALLY) { // Swap places with ally.
        !          1154:                 pmap[defender->xLoc][defender->yLoc].flags &= ~HAS_MONSTER;
        !          1155:                 defender->xLoc = x;
        !          1156:                 defender->yLoc = y;
        !          1157:                 if (monsterAvoids(defender, x, y)) {
        !          1158:                     getQualifyingPathLocNear(&(defender->xLoc), &(defender->yLoc), player.xLoc, player.yLoc, true, forbiddenFlagsForMonster(&(defender->info)), 0, 0, (HAS_PLAYER | HAS_MONSTER | HAS_STAIRS), false);
        !          1159:                 }
        !          1160:                 //getQualifyingLocNear(loc, player.xLoc, player.yLoc, true, NULL, forbiddenFlagsForMonster(&(defender->info)) & ~(T_IS_DF_TRAP | T_IS_DEEP_WATER | T_SPONTANEOUSLY_IGNITES), HAS_MONSTER, false, false);
        !          1161:                 //defender->xLoc = loc[0];
        !          1162:                 //defender->yLoc = loc[1];
        !          1163:                 pmap[defender->xLoc][defender->yLoc].flags |= HAS_MONSTER;
        !          1164:             }
        !          1165:
        !          1166:             if (pmap[player.xLoc][player.yLoc].flags & HAS_ITEM) {
        !          1167:                 pickUpItemAt(player.xLoc, player.yLoc);
        !          1168:                 rogue.disturbed = true;
        !          1169:             }
        !          1170:             refreshDungeonCell(x, y);
        !          1171:             refreshDungeonCell(player.xLoc, player.yLoc);
        !          1172:             playerMoved = true;
        !          1173:
        !          1174:             checkForMissingKeys(x, y);
        !          1175:             if (monsterShouldFall(&player)) {
        !          1176:                 player.bookkeepingFlags |= MB_IS_FALLING;
        !          1177:             }
        !          1178:             moveEntrancedMonsters(direction);
        !          1179:
        !          1180:             // Perform a lunge or flail attack if appropriate.
        !          1181:             for (i=0; i<16; i++) {
        !          1182:                 if (hitList[i]) {
        !          1183:                     if (attack(&player, hitList[i], (rogue.weapon && (rogue.weapon->flags & ITEM_LUNGE_ATTACKS)))) {
        !          1184:                         anyAttackHit = true;
        !          1185:                     }
        !          1186:                 }
        !          1187:             }
        !          1188:             if (hitList[0]) {
        !          1189:                 playerRecoversFromAttacking(anyAttackHit);
        !          1190:             }
        !          1191:
        !          1192:             playerTurnEnded();
        !          1193:         }
        !          1194:     } else if (cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)) {
        !          1195:         i = pmap[newX][newY].layers[layerWithFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)];
        !          1196:         if ((tileCatalog[i].flags & T_OBSTRUCTS_PASSABILITY)
        !          1197:             && (!diagonalBlocked(x, y, newX, newY, false) || !cellHasTMFlag(newX, newY, TM_PROMOTES_WITH_KEY))) {
        !          1198:
        !          1199:             if (!(pmap[newX][newY].flags & DISCOVERED)) {
        !          1200:                 if (!alreadyRecorded) {
        !          1201:                     recordKeystroke(directionKeys[initialDirection], false, false);
        !          1202:                     alreadyRecorded = true;
        !          1203:                 }
        !          1204:                 discoverCell(newX, newY);
        !          1205:                 refreshDungeonCell(newX, newY);
        !          1206:             }
        !          1207:
        !          1208:             messageWithColor(tileCatalog[i].flavorText, &backgroundMessageColor, false);
        !          1209:         }
        !          1210:     }
        !          1211:     return playerMoved;
        !          1212: }
        !          1213:
        !          1214: // replaced in Dijkstra.c:
        !          1215: /*
        !          1216: // returns true if the cell value changed
        !          1217: boolean updateDistanceCell(short **distanceMap, short x, short y) {
        !          1218:     short dir, newX, newY;
        !          1219:     boolean somethingChanged = false;
        !          1220:
        !          1221:     if (distanceMap[x][y] >= 0 && distanceMap[x][y] < 30000) {
        !          1222:         for (dir=0; dir< DIRECTION_COUNT; dir++) {
        !          1223:             newX = x + nbDirs[dir][0];
        !          1224:             newY = y + nbDirs[dir][1];
        !          1225:             if (coordinatesAreInMap(newX, newY)
        !          1226:                 && distanceMap[newX][newY] >= distanceMap[x][y] + 2
        !          1227:                 && !diagonalBlocked(x, y, newX, newY)) {
        !          1228:                 distanceMap[newX][newY] = distanceMap[x][y] + 1;
        !          1229:                 somethingChanged = true;
        !          1230:             }
        !          1231:         }
        !          1232:     }
        !          1233:     return somethingChanged;
        !          1234: }
        !          1235:
        !          1236: void dijkstraScan(short **distanceMap, char passMap[DCOLS][DROWS], boolean allowDiagonals) {
        !          1237:     short i, j, maxDir;
        !          1238:     enum directions dir;
        !          1239:     boolean somethingChanged;
        !          1240:
        !          1241:     maxDir = (allowDiagonals ? 8 : 4);
        !          1242:
        !          1243:     do {
        !          1244:         somethingChanged = false;
        !          1245:         for (i=1; i<DCOLS-1; i++) {
        !          1246:             for (j=1; j<DROWS-1; j++) {
        !          1247:                 if (!passMap || passMap[i][j]) {
        !          1248:                     for (dir = 0; dir < maxDir; dir++) {
        !          1249:                         if (coordinatesAreInMap(i + nbDirs[dir][0], j + nbDirs[dir][1])
        !          1250:                             && (!passMap || passMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]])
        !          1251:                             && distanceMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]] >= distanceMap[i][j] + 2) {
        !          1252:                             distanceMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]] = distanceMap[i][j] + 1;
        !          1253:                             somethingChanged = true;
        !          1254:                         }
        !          1255:                     }
        !          1256:                 }
        !          1257:             }
        !          1258:         }
        !          1259:
        !          1260:
        !          1261:         for (i = DCOLS - 1; i >= 0; i--) {
        !          1262:             for (j = DROWS - 1; j >= 0; j--) {
        !          1263:                 if (!passMap || passMap[i][j]) {
        !          1264:                     for (dir = 0; dir < maxDir; dir++) {
        !          1265:                         if (coordinatesAreInMap(i + nbDirs[dir][0], j + nbDirs[dir][1])
        !          1266:                             && (!passMap || passMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]])
        !          1267:                             && distanceMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]] >= distanceMap[i][j] + 2) {
        !          1268:                             distanceMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]] = distanceMap[i][j] + 1;
        !          1269:                             somethingChanged = true;
        !          1270:                         }
        !          1271:                     }
        !          1272:                 }
        !          1273:             }
        !          1274:         }
        !          1275:     } while (somethingChanged);
        !          1276: }*/
        !          1277:
        !          1278: /*void enqueue(short x, short y, short val, distanceQueue *dQ) {
        !          1279:     short *qX2, *qY2, *qVal2;
        !          1280:
        !          1281:     // if we need to allocate more memory:
        !          1282:     if (dQ->qLen + 1 > dQ->qMaxLen) {
        !          1283:         dQ->qMaxLen *= 2;
        !          1284:         qX2 = realloc(dQ->qX, dQ->qMaxLen);
        !          1285:         if (qX2) {
        !          1286:             free(dQ->qX);
        !          1287:             dQ->qX = qX2;
        !          1288:         } else {
        !          1289:             // out of memory
        !          1290:         }
        !          1291:         qY2 = realloc(dQ->qY, dQ->qMaxLen);
        !          1292:         if (qY2) {
        !          1293:             free(dQ->qY);
        !          1294:             dQ->qY = qY2;
        !          1295:         } else {
        !          1296:             // out of memory
        !          1297:         }
        !          1298:         qVal2 = realloc(dQ->qVal, dQ->qMaxLen);
        !          1299:         if (qVal2) {
        !          1300:             free(dQ->qVal);
        !          1301:             dQ->qVal = qVal2;
        !          1302:         } else {
        !          1303:             // out of memory
        !          1304:         }
        !          1305:     }
        !          1306:
        !          1307:     dQ->qX[dQ->qLen] = x;
        !          1308:     dQ->qY[dQ->qLen] = y;
        !          1309:     (dQ->qVal)[dQ->qLen] = val;
        !          1310:
        !          1311:     dQ->qLen++;
        !          1312:
        !          1313:     if (val < dQ->qMinVal) {
        !          1314:         dQ->qMinVal = val;
        !          1315:         dQ->qMinCount = 1;
        !          1316:     } else if (val == dQ->qMinVal) {
        !          1317:         dQ->qMinCount++;
        !          1318:     }
        !          1319: }
        !          1320:
        !          1321: void updateQueueMinCache(distanceQueue *dQ) {
        !          1322:     short i;
        !          1323:     dQ->qMinCount = 0;
        !          1324:     dQ->qMinVal = 30001;
        !          1325:     for (i = 0; i < dQ->qLen; i++) {
        !          1326:         if (dQ->qVal[i] < dQ->qMinVal) {
        !          1327:             dQ->qMinVal = dQ->qVal[i];
        !          1328:             dQ->qMinCount = 1;
        !          1329:         } else if (dQ->qVal[i] == dQ->qMinVal) {
        !          1330:             dQ->qMinCount++;
        !          1331:         }
        !          1332:     }
        !          1333: }
        !          1334:
        !          1335: // removes the lowest value from the queue, populates x/y/value variables and updates min caching
        !          1336: void dequeue(short *x, short *y, short *val, distanceQueue *dQ) {
        !          1337:     short i, minIndex;
        !          1338:
        !          1339:     if (dQ->qMinCount <= 0) {
        !          1340:         updateQueueMinCache(dQ);
        !          1341:     }
        !          1342:
        !          1343:     *val = dQ->qMinVal;
        !          1344:
        !          1345:     // find the last instance of the minVal
        !          1346:     for (minIndex = dQ->qLen - 1; minIndex >= 0 && dQ->qVal[minIndex] != *val; minIndex--);
        !          1347:
        !          1348:     // populate the return variables
        !          1349:     *x = dQ->qX[minIndex];
        !          1350:     *y = dQ->qY[minIndex];
        !          1351:
        !          1352:     dQ->qLen--;
        !          1353:
        !          1354:     // delete the minValue queue entry
        !          1355:     for (i = minIndex; i < dQ->qLen; i++) {
        !          1356:         dQ->qX[i] = dQ->qX[i+1];
        !          1357:         dQ->qY[i] = dQ->qY[i+1];
        !          1358:         dQ->qVal[i] = dQ->qVal[i+1];
        !          1359:     }
        !          1360:
        !          1361:     // update min values
        !          1362:     dQ->qMinCount--;
        !          1363:     if (!dQ->qMinCount && dQ->qLen) {
        !          1364:         updateQueueMinCache(dQ);
        !          1365:     }
        !          1366:
        !          1367: }
        !          1368:
        !          1369: void dijkstraScan(short **distanceMap, char passMap[DCOLS][DROWS], boolean allowDiagonals) {
        !          1370:     short i, j, maxDir, val;
        !          1371:     enum directions dir;
        !          1372:     distanceQueue dQ;
        !          1373:
        !          1374:     dQ.qMaxLen = DCOLS * DROWS * 1.5;
        !          1375:     dQ.qX = (short *) malloc(dQ.qMaxLen * sizeof(short));
        !          1376:     dQ.qY = (short *) malloc(dQ.qMaxLen * sizeof(short));
        !          1377:     dQ.qVal = (short *) malloc(dQ.qMaxLen * sizeof(short));
        !          1378:     dQ.qLen = 0;
        !          1379:     dQ.qMinVal = 30000;
        !          1380:     dQ.qMinCount = 0;
        !          1381:
        !          1382:     maxDir = (allowDiagonals ? 8 : 4);
        !          1383:
        !          1384:     // seed the queue with the entire map
        !          1385:     for (i=0; i<DCOLS; i++) {
        !          1386:         for (j=0; j<DROWS; j++) {
        !          1387:             if (!passMap || passMap[i][j]) {
        !          1388:                 enqueue(i, j, distanceMap[i][j], &dQ);
        !          1389:             }
        !          1390:         }
        !          1391:     }
        !          1392:
        !          1393:     // iterate through queue updating lowest entries until the queue is empty
        !          1394:     while (dQ.qLen) {
        !          1395:         dequeue(&i, &j, &val, &dQ);
        !          1396:         if (distanceMap[i][j] == val) { // if it hasn't been improved since joining the queue
        !          1397:             for (dir = 0; dir < maxDir; dir++) {
        !          1398:                 if (coordinatesAreInMap(i + nbDirs[dir][0], j + nbDirs[dir][1])
        !          1399:                     && (!passMap || passMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]])
        !          1400:                     && distanceMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]] >= distanceMap[i][j] + 2) {
        !          1401:
        !          1402:                     distanceMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]] = distanceMap[i][j] + 1;
        !          1403:
        !          1404:                     enqueue(i + nbDirs[dir][0], j + nbDirs[dir][1], distanceMap[i][j] + 1, &dQ);
        !          1405:                 }
        !          1406:             }
        !          1407:         }
        !          1408:     }
        !          1409:
        !          1410:     free(dQ.qX);
        !          1411:     free(dQ.qY);
        !          1412:     free(dQ.qVal);
        !          1413: }*/
        !          1414:
        !          1415: /*
        !          1416: void calculateDistances(short **distanceMap, short destinationX, short destinationY, unsigned long blockingTerrainFlags, creature *traveler) {
        !          1417:     short i, j;
        !          1418:     boolean somethingChanged;
        !          1419:
        !          1420:     for (i=0; i<DCOLS; i++) {
        !          1421:         for (j=0; j<DROWS; j++) {
        !          1422:             distanceMap[i][j] = ((traveler && traveler == &player && !(pmap[i][j].flags & (DISCOVERED | MAGIC_MAPPED)))
        !          1423:                                  || ((traveler && monsterAvoids(traveler, i, j))
        !          1424:                                      || cellHasTerrainFlag(i, j, blockingTerrainFlags))) ? -1 : 30000;
        !          1425:         }
        !          1426:     }
        !          1427:
        !          1428:     distanceMap[destinationX][destinationY] = 0;
        !          1429:
        !          1430: //  dijkstraScan(distanceMap);
        !          1431:     do {
        !          1432:         somethingChanged = false;
        !          1433:         for (i=0; i<DCOLS; i++) {
        !          1434:             for (j=0; j<DROWS; j++) {
        !          1435:                 if (updateDistanceCell(distanceMap, i, j)) {
        !          1436:                     somethingChanged = true;
        !          1437:                 }
        !          1438:             }
        !          1439:         }
        !          1440:
        !          1441:
        !          1442:         for (i = DCOLS - 1; i >= 0; i--) {
        !          1443:             for (j = DROWS - 1; j >= 0; j--) {
        !          1444:                 if (updateDistanceCell(distanceMap, i, j)) {
        !          1445:                     somethingChanged = true;
        !          1446:                 }
        !          1447:             }
        !          1448:         }
        !          1449:     } while (somethingChanged);
        !          1450: }*/
        !          1451:
        !          1452: // Returns -1 if there are no beneficial moves.
        !          1453: // If preferDiagonals is true, we will prefer diagonal moves.
        !          1454: // Always rolls downhill on the distance map.
        !          1455: // If monst is provided, do not return a direction pointing to
        !          1456: // a cell that the monster avoids.
        !          1457: short nextStep(short **distanceMap, short x, short y, creature *monst, boolean preferDiagonals) {
        !          1458:     short newX, newY, bestScore;
        !          1459:     enum directions dir, bestDir;
        !          1460:     creature *blocker;
        !          1461:     boolean blocked;
        !          1462:
        !          1463:     brogueAssert(coordinatesAreInMap(x, y));
        !          1464:
        !          1465:     bestScore = 0;
        !          1466:     bestDir = NO_DIRECTION;
        !          1467:
        !          1468:     for (dir = (preferDiagonals ? 7 : 0);
        !          1469:          (preferDiagonals ? dir >= 0 : dir < DIRECTION_COUNT);
        !          1470:          (preferDiagonals ? dir-- : dir++)) {
        !          1471:
        !          1472:         newX = x + nbDirs[dir][0];
        !          1473:         newY = y + nbDirs[dir][1];
        !          1474:
        !          1475:         brogueAssert(coordinatesAreInMap(newX, newY));
        !          1476:         if (coordinatesAreInMap(newX, newY)) {
        !          1477:             blocked = false;
        !          1478:             blocker = monsterAtLoc(newX, newY);
        !          1479:             if (monst
        !          1480:                 && monsterAvoids(monst, newX, newY)) {
        !          1481:
        !          1482:                 blocked = true;
        !          1483:             } else if (monst
        !          1484:                        && blocker
        !          1485:                        && !canPass(monst, blocker)
        !          1486:                        && !monstersAreTeammates(monst, blocker)
        !          1487:                        && !monstersAreEnemies(monst, blocker)) {
        !          1488:                 blocked = true;
        !          1489:             }
        !          1490:             if ((distanceMap[x][y] - distanceMap[newX][newY]) > bestScore
        !          1491:                 && !diagonalBlocked(x, y, newX, newY, monst == &player)
        !          1492:                 && knownToPlayerAsPassableOrSecretDoor(newX, newY)
        !          1493:                 && !blocked) {
        !          1494:
        !          1495:                 bestDir = dir;
        !          1496:                 bestScore = distanceMap[x][y] - distanceMap[newX][newY];
        !          1497:             }
        !          1498:         }
        !          1499:     }
        !          1500:     return bestDir;
        !          1501: }
        !          1502:
        !          1503: void displayRoute(short **distanceMap, boolean removeRoute) {
        !          1504:     short currentX = player.xLoc, currentY = player.yLoc, dir, newX, newY;
        !          1505:     boolean advanced;
        !          1506:
        !          1507:     if (distanceMap[player.xLoc][player.yLoc] < 0 || distanceMap[player.xLoc][player.yLoc] == 30000) {
        !          1508:         return;
        !          1509:     }
        !          1510:     do {
        !          1511:         if (removeRoute) {
        !          1512:             refreshDungeonCell(currentX, currentY);
        !          1513:         } else {
        !          1514:             hiliteCell(currentX, currentY, &hiliteColor, 50, true);
        !          1515:         }
        !          1516:         advanced = false;
        !          1517:         for (dir = 7; dir >= 0; dir--) {
        !          1518:             newX = currentX + nbDirs[dir][0];
        !          1519:             newY = currentY + nbDirs[dir][1];
        !          1520:             if (coordinatesAreInMap(newX, newY)
        !          1521:                 && distanceMap[newX][newY] >= 0 && distanceMap[newX][newY] < distanceMap[currentX][currentY]
        !          1522:                 && !diagonalBlocked(currentX, currentY, newX, newY, true)) {
        !          1523:
        !          1524:                 currentX = newX;
        !          1525:                 currentY = newY;
        !          1526:                 advanced = true;
        !          1527:                 break;
        !          1528:             }
        !          1529:         }
        !          1530:     } while (advanced);
        !          1531: }
        !          1532:
        !          1533: void travelRoute(short path[1000][2], short steps) {
        !          1534:     short i, j;
        !          1535:     short dir;
        !          1536:     creature *monst;
        !          1537:
        !          1538:     brogueAssert(!rogue.playbackMode);
        !          1539:
        !          1540:     rogue.disturbed = false;
        !          1541:     rogue.automationActive = true;
        !          1542:
        !          1543:     for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
        !          1544:         if (canSeeMonster(monst)) {
        !          1545:             monst->bookkeepingFlags |= MB_ALREADY_SEEN;
        !          1546:         } else {
        !          1547:             monst->bookkeepingFlags &= ~MB_ALREADY_SEEN;
        !          1548:         }
        !          1549:     }
        !          1550:
        !          1551:     for (i=0; i < steps && !rogue.disturbed; i++) {
        !          1552:         for (j = i + 1; j < steps - 1; j++) {
        !          1553:             // Check to see if the path has become obstructed or avoided since the last time we saw it.
        !          1554:             if (diagonalBlocked(path[j-1][0], path[j-1][1], path[j][0], path[j][1], true)
        !          1555:                 || monsterAvoids(&player, path[j][0], path[j][1])) {
        !          1556:
        !          1557:                 rogue.disturbed = true;
        !          1558:                 break;
        !          1559:             }
        !          1560:         }
        !          1561:         for (dir = 0; dir < DIRECTION_COUNT && !rogue.disturbed; dir++) {
        !          1562:             if (player.xLoc + nbDirs[dir][0] == path[i][0]
        !          1563:                 && player.yLoc + nbDirs[dir][1] == path[i][1]) {
        !          1564:
        !          1565:                 if (!playerMoves(dir)) {
        !          1566:                     rogue.disturbed = true;
        !          1567:                 }
        !          1568:                 if (pauseBrogue(25)) {
        !          1569:                     rogue.disturbed = true;
        !          1570:                 }
        !          1571:                 break;
        !          1572:             }
        !          1573:         }
        !          1574:     }
        !          1575:     rogue.disturbed = true;
        !          1576:     rogue.automationActive = false;
        !          1577:     updateFlavorText();
        !          1578: }
        !          1579:
        !          1580: void travelMap(short **distanceMap) {
        !          1581:     short currentX = player.xLoc, currentY = player.yLoc, dir, newX, newY;
        !          1582:     boolean advanced;
        !          1583:
        !          1584:     rogue.disturbed = false;
        !          1585:     rogue.automationActive = true;
        !          1586:
        !          1587:     if (distanceMap[player.xLoc][player.yLoc] < 0 || distanceMap[player.xLoc][player.yLoc] == 30000) {
        !          1588:         return;
        !          1589:     }
        !          1590:     do {
        !          1591:         advanced = false;
        !          1592:         for (dir = 7; dir >= 0; dir--) {
        !          1593:             newX = currentX + nbDirs[dir][0];
        !          1594:             newY = currentY + nbDirs[dir][1];
        !          1595:             if (coordinatesAreInMap(newX, newY)
        !          1596:                 && distanceMap[newX][newY] >= 0
        !          1597:                 && distanceMap[newX][newY] < distanceMap[currentX][currentY]
        !          1598:                 && !diagonalBlocked(currentX, currentY, newX, newY, true)) {
        !          1599:
        !          1600:                 if (!playerMoves(dir)) {
        !          1601:                     rogue.disturbed = true;
        !          1602:                 }
        !          1603:                 if (pauseBrogue(500)) {
        !          1604:                     rogue.disturbed = true;
        !          1605:                 }
        !          1606:                 currentX = newX;
        !          1607:                 currentY = newY;
        !          1608:                 advanced = true;
        !          1609:                 break;
        !          1610:             }
        !          1611:         }
        !          1612:     } while (advanced && !rogue.disturbed);
        !          1613:     rogue.disturbed = true;
        !          1614:     rogue.automationActive = false;
        !          1615:     updateFlavorText();
        !          1616: }
        !          1617:
        !          1618: void travel(short x, short y, boolean autoConfirm) {
        !          1619:     short **distanceMap, i;
        !          1620:     rogueEvent theEvent;
        !          1621:     unsigned short staircaseConfirmKey;
        !          1622:
        !          1623:     confirmMessages();
        !          1624:
        !          1625:     if (D_WORMHOLING) {
        !          1626:         recordMouseClick(mapToWindowX(x), mapToWindowY(y), true, false);
        !          1627:         pmap[player.xLoc][player.yLoc].flags &= ~HAS_PLAYER;
        !          1628:         refreshDungeonCell(player.xLoc, player.yLoc);
        !          1629:         player.xLoc = x;
        !          1630:         player.yLoc = y;
        !          1631:         pmap[x][y].flags |= HAS_PLAYER;
        !          1632:         updatePlayerUnderwaterness();
        !          1633:         refreshDungeonCell(x, y);
        !          1634:         updateVision(true);
        !          1635:         return;
        !          1636:     }
        !          1637:
        !          1638:     if (abs(player.xLoc - x) + abs(player.yLoc - y) == 1) {
        !          1639:         // targeting a cardinal neighbor
        !          1640:         for (i=0; i<4; i++) {
        !          1641:             if (nbDirs[i][0] == (x - player.xLoc) && nbDirs[i][1] == (y - player.yLoc)) {
        !          1642:                 playerMoves(i);
        !          1643:                 break;
        !          1644:             }
        !          1645:         }
        !          1646:         return;
        !          1647:     }
        !          1648:
        !          1649:     if (!(pmap[x][y].flags & (DISCOVERED | MAGIC_MAPPED))) {
        !          1650:         message("You have not explored that location.", false);
        !          1651:         return;
        !          1652:     }
        !          1653:
        !          1654:     distanceMap = allocGrid();
        !          1655:
        !          1656:     calculateDistances(distanceMap, x, y, 0, &player, false, false);
        !          1657:     if (distanceMap[player.xLoc][player.yLoc] < 30000) {
        !          1658:         if (autoConfirm) {
        !          1659:             travelMap(distanceMap);
        !          1660:             //refreshSideBar(-1, -1, false);
        !          1661:         } else {
        !          1662:             if (rogue.upLoc[0] == x && rogue.upLoc[1] == y) {
        !          1663:                 staircaseConfirmKey = ASCEND_KEY;
        !          1664:             } else if (rogue.downLoc[0] == x && rogue.downLoc[1] == y) {
        !          1665:                 staircaseConfirmKey = DESCEND_KEY;
        !          1666:             } else {
        !          1667:                 staircaseConfirmKey = 0;
        !          1668:             }
        !          1669:             displayRoute(distanceMap, false);
        !          1670:             message("Travel this route? (y/n)", false);
        !          1671:
        !          1672:             do {
        !          1673:                 nextBrogueEvent(&theEvent, true, false, false);
        !          1674:             } while (theEvent.eventType != MOUSE_UP && theEvent.eventType != KEYSTROKE);
        !          1675:
        !          1676:             displayRoute(distanceMap, true); // clear route display
        !          1677:             confirmMessages();
        !          1678:
        !          1679:             if ((theEvent.eventType == MOUSE_UP && windowToMapX(theEvent.param1) == x && windowToMapY(theEvent.param2) == y)
        !          1680:                 || (theEvent.eventType == KEYSTROKE && (theEvent.param1 == 'Y' || theEvent.param1 == 'y'
        !          1681:                                                         || theEvent.param1 == RETURN_KEY
        !          1682:                                                         || (theEvent.param1 == staircaseConfirmKey
        !          1683:                                                             && theEvent.param1 != 0)))) {
        !          1684:                 travelMap(distanceMap);
        !          1685:                 //refreshSideBar(-1, -1, false);
        !          1686:                 commitDraws();
        !          1687:             } else if (theEvent.eventType == MOUSE_UP) {
        !          1688:                 executeMouseClick(&theEvent);
        !          1689:             }
        !          1690:         }
        !          1691: //      if (player.xLoc == x && player.yLoc == y) {
        !          1692: //          rogue.cursorLoc[0] = rogue.cursorLoc[1] = 0;
        !          1693: //      } else {
        !          1694: //          rogue.cursorLoc[0] = x;
        !          1695: //          rogue.cursorLoc[1] = y;
        !          1696: //      }
        !          1697:     } else {
        !          1698:         rogue.cursorLoc[0] = rogue.cursorLoc[1] = -1;
        !          1699:         message("No path is available.", false);
        !          1700:     }
        !          1701:     freeGrid(distanceMap);
        !          1702: }
        !          1703:
        !          1704: void populateGenericCostMap(short **costMap) {
        !          1705:     short i, j;
        !          1706:
        !          1707:     for (i=0; i<DCOLS; i++) {
        !          1708:         for (j=0; j<DROWS; j++) {
        !          1709:             if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)
        !          1710:                 && (!cellHasTMFlag(i, j, TM_IS_SECRET) || (discoveredTerrainFlagsAtLoc(i, j) & T_OBSTRUCTS_PASSABILITY))) {
        !          1711:
        !          1712:                 costMap[i][j] = cellHasTerrainFlag(i, j, T_OBSTRUCTS_DIAGONAL_MOVEMENT) ? PDS_OBSTRUCTION : PDS_FORBIDDEN;
        !          1713:             } else if (cellHasTerrainFlag(i, j, T_PATHING_BLOCKER & ~T_OBSTRUCTS_PASSABILITY)) {
        !          1714:                 costMap[i][j] = PDS_FORBIDDEN;
        !          1715:             } else {
        !          1716:                 costMap[i][j] = 1;
        !          1717:             }
        !          1718:         }
        !          1719:     }
        !          1720: }
        !          1721:
        !          1722: void getLocationFlags(const short x, const short y,
        !          1723:                       unsigned long *tFlags, unsigned long *TMFlags, unsigned long *cellFlags,
        !          1724:                       const boolean limitToPlayerKnowledge) {
        !          1725:     if (limitToPlayerKnowledge
        !          1726:         && (pmap[x][y].flags & (DISCOVERED | MAGIC_MAPPED))
        !          1727:         && !playerCanSee(x, y)) {
        !          1728:
        !          1729:         if (tFlags) {
        !          1730:             *tFlags = pmap[x][y].rememberedTerrainFlags;
        !          1731:         }
        !          1732:         if (TMFlags) {
        !          1733:             *TMFlags = pmap[x][y].rememberedTMFlags;
        !          1734:         }
        !          1735:         if (cellFlags) {
        !          1736:             *cellFlags = pmap[x][y].rememberedCellFlags;
        !          1737:         }
        !          1738:     } else {
        !          1739:         if (tFlags) {
        !          1740:             *tFlags = terrainFlags(x, y);
        !          1741:         }
        !          1742:         if (TMFlags) {
        !          1743:             *TMFlags = terrainMechFlags(x, y);
        !          1744:         }
        !          1745:         if (cellFlags) {
        !          1746:             *cellFlags = pmap[x][y].flags;
        !          1747:         }
        !          1748:     }
        !          1749: }
        !          1750:
        !          1751: void populateCreatureCostMap(short **costMap, creature *monst) {
        !          1752:     short i, j, unexploredCellCost;
        !          1753:     creature *currentTenant;
        !          1754:     item *theItem;
        !          1755:     unsigned long tFlags, cFlags;
        !          1756:
        !          1757:     unexploredCellCost = 10 + (clamp(rogue.depthLevel, 5, 15) - 5) * 2;
        !          1758:
        !          1759:     for (i=0; i<DCOLS; i++) {
        !          1760:         for (j=0; j<DROWS; j++) {
        !          1761:             if (monst == &player && !(pmap[i][j].flags & (DISCOVERED | MAGIC_MAPPED))) {
        !          1762:                 costMap[i][j] = PDS_OBSTRUCTION;
        !          1763:                 continue;
        !          1764:             }
        !          1765:
        !          1766:             getLocationFlags(i, j, &tFlags, NULL, &cFlags, monst == &player);
        !          1767:
        !          1768:             if ((tFlags & T_OBSTRUCTS_PASSABILITY)
        !          1769:                  && (!cellHasTMFlag(i, j, TM_IS_SECRET) || (discoveredTerrainFlagsAtLoc(i, j) & T_OBSTRUCTS_PASSABILITY) || monst == &player)) {
        !          1770:
        !          1771:                 costMap[i][j] = (tFlags & T_OBSTRUCTS_DIAGONAL_MOVEMENT) ? PDS_OBSTRUCTION : PDS_FORBIDDEN;
        !          1772:                 continue;
        !          1773:             }
        !          1774:
        !          1775:             if ((tFlags & T_LAVA_INSTA_DEATH)
        !          1776:                 && !(monst->info.flags & (MONST_IMMUNE_TO_FIRE | MONST_FLIES | MONST_INVULNERABLE))
        !          1777:                 && (monst->status[STATUS_LEVITATING] || monst->status[STATUS_IMMUNE_TO_FIRE])
        !          1778:                 && max(monst->status[STATUS_LEVITATING], monst->status[STATUS_IMMUNE_TO_FIRE]) < (rogue.mapToShore[i][j] + distanceBetween(i, j, monst->xLoc, monst->yLoc) * monst->movementSpeed / 100)) {
        !          1779:                 // Only a temporary effect will permit the monster to survive the lava, and the remaining duration either isn't
        !          1780:                 // enough to get it to the spot, or it won't suffice to let it return to shore if it does get there.
        !          1781:                 // Treat these locations as obstacles.
        !          1782:                 costMap[i][j] = PDS_FORBIDDEN;
        !          1783:                 continue;
        !          1784:             }
        !          1785:
        !          1786:             if (((tFlags & T_AUTO_DESCENT) || (tFlags & T_IS_DEEP_WATER) && !(monst->info.flags & MONST_IMMUNE_TO_WATER))
        !          1787:                 && !(monst->info.flags & MONST_FLIES)
        !          1788:                 && (monst->status[STATUS_LEVITATING])
        !          1789:                 && monst->status[STATUS_LEVITATING] < (rogue.mapToShore[i][j] + distanceBetween(i, j, monst->xLoc, monst->yLoc) * monst->movementSpeed / 100)) {
        !          1790:                 // Only a temporary effect will permit the monster to levitate over the chasm/water, and the remaining duration either isn't
        !          1791:                 // enough to get it to the spot, or it won't suffice to let it return to shore if it does get there.
        !          1792:                 // Treat these locations as obstacles.
        !          1793:                 costMap[i][j] = PDS_FORBIDDEN;
        !          1794:                 continue;
        !          1795:             }
        !          1796:
        !          1797:             if (monsterAvoids(monst, i, j)) {
        !          1798:                 costMap[i][j] = PDS_FORBIDDEN;
        !          1799:                 continue;
        !          1800:             }
        !          1801:
        !          1802:             if (cFlags & HAS_MONSTER) {
        !          1803:                 currentTenant = monsterAtLoc(i, j);
        !          1804:                 if (currentTenant
        !          1805:                     && (currentTenant->info.flags & (MONST_IMMUNE_TO_WEAPONS | MONST_INVULNERABLE))
        !          1806:                     && !canPass(monst, currentTenant)) {
        !          1807:
        !          1808:                     costMap[i][j] = PDS_FORBIDDEN;
        !          1809:                     continue;
        !          1810:                 }
        !          1811:             }
        !          1812:
        !          1813:             if ((cFlags & KNOWN_TO_BE_TRAP_FREE)
        !          1814:                 || (monst != &player && monst->creatureState != MONSTER_ALLY)) {
        !          1815:
        !          1816:                 costMap[i][j] = 10;
        !          1817:             } else {
        !          1818:                 // Player and allies give locations that are known to be free of traps
        !          1819:                 // an advantage that increases with depth level, based on the depths
        !          1820:                 // at which traps are generated.
        !          1821:                 costMap[i][j] = unexploredCellCost;
        !          1822:             }
        !          1823:
        !          1824:             if (!(monst->info.flags & MONST_INVULNERABLE)) {
        !          1825:                 if ((tFlags & T_CAUSES_NAUSEA)
        !          1826:                     || cellHasTMFlag(i, j, TM_PROMOTES_ON_ITEM_PICKUP)
        !          1827:                     || (tFlags & T_ENTANGLES) && !(monst->info.flags & MONST_IMMUNE_TO_WEBS)) {
        !          1828:
        !          1829:                     costMap[i][j] += 20;
        !          1830:                 }
        !          1831:             }
        !          1832:
        !          1833:             if (monst == &player) {
        !          1834:                 theItem = itemAtLoc(i, j);
        !          1835:                 if (theItem && (theItem->flags & ITEM_PLAYER_AVOIDS)) {
        !          1836:                     costMap[i][j] += 10;
        !          1837:                 }
        !          1838:             }
        !          1839:         }
        !          1840:     }
        !          1841: }
        !          1842:
        !          1843: enum directions adjacentFightingDir() {
        !          1844:     short newX, newY;
        !          1845:     enum directions dir;
        !          1846:     creature *monst;
        !          1847:
        !          1848:     if (cellHasTerrainFlag(player.xLoc, player.yLoc, T_OBSTRUCTS_PASSABILITY)) {
        !          1849:         return NO_DIRECTION;
        !          1850:     }
        !          1851:     for (dir = 0; dir < DIRECTION_COUNT; dir++) {
        !          1852:         newX = player.xLoc + nbDirs[dir][0];
        !          1853:         newY = player.yLoc + nbDirs[dir][1];
        !          1854:         monst = monsterAtLoc(newX, newY);
        !          1855:         if (monst
        !          1856:             && canSeeMonster(monst)
        !          1857:             && (!diagonalBlocked(player.xLoc, player.yLoc, newX, newY, false) || (monst->info.flags & MONST_ATTACKABLE_THRU_WALLS))
        !          1858:             && monstersAreEnemies(&player, monst)
        !          1859:             && !(monst->info.flags & (MONST_IMMUNE_TO_WEAPONS | MONST_INVULNERABLE))) {
        !          1860:
        !          1861:             return dir;
        !          1862:         }
        !          1863:     }
        !          1864:     return NO_DIRECTION;
        !          1865: }
        !          1866:
        !          1867: #define exploreGoalValue(x, y)  (0 - abs((x) - DCOLS / 2) / 3 - abs((x) - DCOLS / 2) / 4)
        !          1868:
        !          1869: void getExploreMap(short **map, boolean headingToStairs) {// calculate explore map
        !          1870:     short i, j;
        !          1871:     short **costMap;
        !          1872:     item *theItem;
        !          1873:
        !          1874:     costMap = allocGrid();
        !          1875:     populateCreatureCostMap(costMap, &player);
        !          1876:
        !          1877:     for (i=0; i<DCOLS; i++) {
        !          1878:         for (j=0; j<DROWS; j++) {
        !          1879:             map[i][j] = 30000; // Can be overridden later.
        !          1880:             theItem = itemAtLoc(i, j);
        !          1881:             if (!(pmap[i][j].flags & DISCOVERED)) {
        !          1882:                 if ((pmap[i][j].flags & MAGIC_MAPPED)
        !          1883:                     && (tileCatalog[pmap[i][j].layers[DUNGEON]].flags | tileCatalog[pmap[i][j].layers[LIQUID]].flags) & T_PATHING_BLOCKER) {
        !          1884:                     // Magic-mapped cells revealed as obstructions should be treated as such even though they're not discovered.
        !          1885:                     costMap[i][j] = cellHasTerrainFlag(i, j, T_OBSTRUCTS_DIAGONAL_MOVEMENT) ? PDS_OBSTRUCTION : PDS_FORBIDDEN;
        !          1886:                 } else {
        !          1887:                     costMap[i][j] = 1;
        !          1888:                     map[i][j] = exploreGoalValue(i, j);
        !          1889:                 }
        !          1890:             } else if (theItem
        !          1891:                        && !monsterAvoids(&player, i, j)) {
        !          1892:                 if (theItem->flags & ITEM_PLAYER_AVOIDS) {
        !          1893:                     costMap[i][j] = 20;
        !          1894:                 } else {
        !          1895:                     costMap[i][j] = 1;
        !          1896:                     map[i][j] = exploreGoalValue(i, j) - 10;
        !          1897:                 }
        !          1898:             }
        !          1899:         }
        !          1900:     }
        !          1901:
        !          1902:     costMap[rogue.downLoc[0]][rogue.downLoc[1]] = 100;
        !          1903:     costMap[rogue.upLoc[0]][rogue.upLoc[1]]     = 100;
        !          1904:
        !          1905:     if (headingToStairs) {
        !          1906:         map[rogue.downLoc[0]][rogue.downLoc[1]] = 0; // head to the stairs
        !          1907:     }
        !          1908:
        !          1909:     dijkstraScan(map, costMap, true);
        !          1910:
        !          1911:     //displayGrid(costMap);
        !          1912:     freeGrid(costMap);
        !          1913: }
        !          1914:
        !          1915: boolean explore(short frameDelay) {
        !          1916:     short **distanceMap;
        !          1917:     short path[1000][2], steps;
        !          1918:     boolean madeProgress, headingToStairs;
        !          1919:     enum directions dir;
        !          1920:     creature *monst;
        !          1921:
        !          1922:     // Explore commands should never be written to a recording.
        !          1923:     // Instead, the elemental movement commands that compose it
        !          1924:     // should be written individually.
        !          1925:     brogueAssert(!rogue.playbackMode);
        !          1926:
        !          1927:     clearCursorPath();
        !          1928:
        !          1929:     madeProgress    = false;
        !          1930:     headingToStairs = false;
        !          1931:
        !          1932:     if (player.status[STATUS_CONFUSED]) {
        !          1933:         message("Not while you're confused.", false);
        !          1934:         return false;
        !          1935:     }
        !          1936:     if (cellHasTerrainFlag(player.xLoc, player.yLoc, T_OBSTRUCTS_PASSABILITY)) {
        !          1937:         message("Not while you're trapped.", false);
        !          1938:         return false;
        !          1939:     }
        !          1940:
        !          1941:     for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
        !          1942:         if (canSeeMonster(monst)) {
        !          1943:             monst->bookkeepingFlags |= MB_ALREADY_SEEN;
        !          1944:         } else {
        !          1945:             monst->bookkeepingFlags &= ~MB_ALREADY_SEEN;
        !          1946:         }
        !          1947:     }
        !          1948:
        !          1949:     // fight any adjacent enemies
        !          1950:     dir = adjacentFightingDir();
        !          1951:     if (dir != NO_DIRECTION
        !          1952:         && startFighting(dir, (player.status[STATUS_HALLUCINATING] ? true : false))) {
        !          1953:
        !          1954:         return true;
        !          1955:     }
        !          1956:
        !          1957:     if (!rogue.autoPlayingLevel) {
        !          1958:         message(KEYBOARD_LABELS ? "Exploring... press any key to stop." : "Exploring... touch anywhere to stop.",
        !          1959:                 false);
        !          1960:         // A little hack so the exploring message remains bright while exploring and then auto-dims when
        !          1961:         // another message is displayed:
        !          1962:         confirmMessages();
        !          1963:         printString(KEYBOARD_LABELS ? "Exploring... press any key to stop." : "Exploring... touch anywhere to stop.",
        !          1964:                     mapToWindowX(0), mapToWindowY(-1), &white, &black, NULL);
        !          1965:     }
        !          1966:     rogue.disturbed = false;
        !          1967:     rogue.automationActive = true;
        !          1968:
        !          1969:     distanceMap = allocGrid();
        !          1970:     do {
        !          1971:         // fight any adjacent enemies
        !          1972:         dir = adjacentFightingDir();
        !          1973:         if (dir != NO_DIRECTION) {
        !          1974:             startFighting(dir, (player.status[STATUS_HALLUCINATING] ? true : false));
        !          1975:             if (rogue.disturbed) {
        !          1976:                 madeProgress = true;
        !          1977:                 continue;
        !          1978:             }
        !          1979:         }
        !          1980:         if (rogue.disturbed) {
        !          1981:             continue;
        !          1982:         }
        !          1983:
        !          1984:         getExploreMap(distanceMap, headingToStairs);
        !          1985:
        !          1986:         // hilite path
        !          1987:         steps = getPlayerPathOnMap(path, distanceMap, player.xLoc, player.yLoc);
        !          1988:         hilitePath(path, steps, false);
        !          1989:
        !          1990:         // take a step
        !          1991:         dir = nextStep(distanceMap, player.xLoc, player.yLoc, NULL, false);
        !          1992:
        !          1993:         if (!headingToStairs && rogue.autoPlayingLevel && dir == NO_DIRECTION) {
        !          1994:             headingToStairs = true;
        !          1995:             continue;
        !          1996:         }
        !          1997:
        !          1998:         refreshSideBar(-1, -1, false);
        !          1999:
        !          2000:         if (dir == NO_DIRECTION) {
        !          2001:             rogue.disturbed = true;
        !          2002:         } else if (!playerMoves(dir)) {
        !          2003:             rogue.disturbed = true;
        !          2004:         } else {
        !          2005:             madeProgress = true;
        !          2006:             if (pauseBrogue(frameDelay)) {
        !          2007:                 rogue.disturbed = true;
        !          2008:                 rogue.autoPlayingLevel = false;
        !          2009:             }
        !          2010:         }
        !          2011:         hilitePath(path, steps, true);
        !          2012:     } while (!rogue.disturbed);
        !          2013:     //clearCursorPath();
        !          2014:     rogue.automationActive = false;
        !          2015:     refreshSideBar(-1, -1, false);
        !          2016:     freeGrid(distanceMap);
        !          2017:     return madeProgress;
        !          2018: }
        !          2019:
        !          2020: void autoPlayLevel(boolean fastForward) {
        !          2021:     boolean madeProgress;
        !          2022:
        !          2023:     rogue.autoPlayingLevel = true;
        !          2024:
        !          2025:     confirmMessages();
        !          2026:     message(KEYBOARD_LABELS ? "Playing... press any key to stop." : "Playing... touch anywhere to stop.", false);
        !          2027:
        !          2028:     // explore until we are not making progress
        !          2029:     do {
        !          2030:         madeProgress = explore(fastForward ? 1 : 50);
        !          2031:         //refreshSideBar(-1, -1, false);
        !          2032:
        !          2033:         if (!madeProgress && rogue.downLoc[0] == player.xLoc && rogue.downLoc[1] == player.yLoc) {
        !          2034:             useStairs(1);
        !          2035:             madeProgress = true;
        !          2036:         }
        !          2037:     } while (madeProgress && rogue.autoPlayingLevel);
        !          2038:
        !          2039:     confirmMessages();
        !          2040:
        !          2041:     rogue.autoPlayingLevel = false;
        !          2042: }
        !          2043:
        !          2044: short directionOfKeypress(unsigned short ch) {
        !          2045:     switch (ch) {
        !          2046:         case LEFT_KEY:
        !          2047:         case LEFT_ARROW:
        !          2048:         case NUMPAD_4:
        !          2049:             return LEFT;
        !          2050:         case RIGHT_KEY:
        !          2051:         case RIGHT_ARROW:
        !          2052:         case NUMPAD_6:
        !          2053:             return RIGHT;
        !          2054:         case UP_KEY:
        !          2055:         case UP_ARROW:
        !          2056:         case NUMPAD_8:
        !          2057:             return UP;
        !          2058:         case DOWN_KEY:
        !          2059:         case DOWN_ARROW:
        !          2060:         case NUMPAD_2:
        !          2061:             return DOWN;
        !          2062:         case UPLEFT_KEY:
        !          2063:         case NUMPAD_7:
        !          2064:             return UPLEFT;
        !          2065:         case UPRIGHT_KEY:
        !          2066:         case NUMPAD_9:
        !          2067:             return UPRIGHT;
        !          2068:         case DOWNLEFT_KEY:
        !          2069:         case NUMPAD_1:
        !          2070:             return DOWNLEFT;
        !          2071:         case DOWNRIGHT_KEY:
        !          2072:         case NUMPAD_3:
        !          2073:             return DOWNRIGHT;
        !          2074:         default:
        !          2075:             return -1;
        !          2076:     }
        !          2077: }
        !          2078:
        !          2079: boolean startFighting(enum directions dir, boolean tillDeath) {
        !          2080:     short x, y, expectedDamage;
        !          2081:     creature *monst;
        !          2082:
        !          2083:     x = player.xLoc + nbDirs[dir][0];
        !          2084:     y = player.yLoc + nbDirs[dir][1];
        !          2085:     monst = monsterAtLoc(x, y);
        !          2086:     if (monst->info.flags & (MONST_IMMUNE_TO_WEAPONS | MONST_INVULNERABLE)) {
        !          2087:         return false;
        !          2088:     }
        !          2089:     expectedDamage = monst->info.damage.upperBound * monsterDamageAdjustmentAmount(monst) / FP_FACTOR;
        !          2090:     if (rogue.easyMode) {
        !          2091:         expectedDamage /= 5;
        !          2092:     }
        !          2093:     rogue.blockCombatText = true;
        !          2094:     rogue.disturbed = false;
        !          2095:     do {
        !          2096:         if (!playerMoves(dir)) {
        !          2097:             break;
        !          2098:         }
        !          2099:         if (pauseBrogue(1)) {
        !          2100:             break;
        !          2101:         }
        !          2102:     } while (!rogue.disturbed && !rogue.gameHasEnded && (tillDeath || player.currentHP > expectedDamage)
        !          2103:              && (pmap[x][y].flags & HAS_MONSTER) && monsterAtLoc(x, y) == monst);
        !          2104:
        !          2105:     rogue.blockCombatText = false;
        !          2106:     return rogue.disturbed;
        !          2107: }
        !          2108:
        !          2109: boolean isDisturbed(short x, short y) {
        !          2110:     short i;
        !          2111:     creature *monst;
        !          2112:     for (i=0; i< DIRECTION_COUNT; i++) {
        !          2113:         monst = monsterAtLoc(x + nbDirs[i][0], y + nbDirs[i][1]);
        !          2114:         if (pmap[x + nbDirs[i][0]][y + nbDirs[i][1]].flags & (HAS_ITEM)) {
        !          2115:             // Do not trigger for submerged or invisible or unseen monsters.
        !          2116:             return true;
        !          2117:         }
        !          2118:         if (monst
        !          2119:             && !(monst->creatureState == MONSTER_ALLY)
        !          2120:             && (canSeeMonster(monst) || monsterRevealed(monst))) {
        !          2121:             // Do not trigger for submerged or invisible or unseen monsters.
        !          2122:             return true;
        !          2123:         }
        !          2124:     }
        !          2125:     return false;
        !          2126: }
        !          2127:
        !          2128: void discover(short x, short y) {
        !          2129:     enum dungeonLayers layer;
        !          2130:     dungeonFeature *feat;
        !          2131:     if (cellHasTMFlag(x, y, TM_IS_SECRET)) {
        !          2132:
        !          2133:         for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
        !          2134:             if (tileCatalog[pmap[x][y].layers[layer]].mechFlags & TM_IS_SECRET) {
        !          2135:                 feat = &dungeonFeatureCatalog[tileCatalog[pmap[x][y].layers[layer]].discoverType];
        !          2136:                 pmap[x][y].layers[layer] = (layer == DUNGEON ? FLOOR : NOTHING);
        !          2137:                 spawnDungeonFeature(x, y, feat, true, false);
        !          2138:             }
        !          2139:         }
        !          2140:         refreshDungeonCell(x, y);
        !          2141:
        !          2142:         if (playerCanSee(x, y)) {
        !          2143:             rogue.disturbed = true;
        !          2144:         }
        !          2145:     }
        !          2146: }
        !          2147:
        !          2148: // returns true if found anything
        !          2149: boolean search(short searchStrength) {
        !          2150:     short i, j, radius, x, y, percent;
        !          2151:     boolean foundSomething = false;
        !          2152:
        !          2153:     radius = searchStrength / 10;
        !          2154:     x = player.xLoc;
        !          2155:     y = player.yLoc;
        !          2156:
        !          2157:     for (i = x - radius; i <= x + radius; i++) {
        !          2158:         for (j = y - radius; j <= y + radius; j++) {
        !          2159:             if (coordinatesAreInMap(i, j)
        !          2160:                 && playerCanDirectlySee(i, j)) {
        !          2161:
        !          2162:                 percent = searchStrength - distanceBetween(x, y, i, j) * 10;
        !          2163:                 if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)) {
        !          2164:                     percent = percent * 2/3;
        !          2165:                 }
        !          2166:                 if (percent >= 100) {
        !          2167:                     pmap[i][j].flags |= KNOWN_TO_BE_TRAP_FREE;
        !          2168:                 }
        !          2169:                 percent = min(percent, 100);
        !          2170:                 if (cellHasTMFlag(i, j, TM_IS_SECRET)) {
        !          2171:                     if (rand_percent(percent)) {
        !          2172:                         discover(i, j);
        !          2173:                         foundSomething = true;
        !          2174:                     }
        !          2175:                 }
        !          2176:             }
        !          2177:         }
        !          2178:     }
        !          2179:     return foundSomething;
        !          2180: }
        !          2181:
        !          2182: boolean proposeOrConfirmLocation(short x, short y, char *failureMessage) {
        !          2183:     boolean retval = false;
        !          2184:     if (player.xLoc == x && player.yLoc == y) {
        !          2185:         message("you are already there.", false);
        !          2186:     } else if (pmap[x][y].flags & (DISCOVERED | MAGIC_MAPPED)) {
        !          2187:         if (rogue.cursorLoc[0] == x && rogue.cursorLoc[1] == y) {
        !          2188:             retval = true;
        !          2189:         } else {
        !          2190:             rogue.cursorLoc[0] = x;
        !          2191:             rogue.cursorLoc[1] = y;
        !          2192:         }
        !          2193:     } else {
        !          2194:         message(failureMessage, false);
        !          2195:     }
        !          2196:     return retval;
        !          2197: }
        !          2198:
        !          2199: boolean useStairs(short stairDirection) {
        !          2200:     boolean succeeded = false;
        !          2201:     //cellDisplayBuffer fromBuf[COLS][ROWS], toBuf[COLS][ROWS];
        !          2202:
        !          2203:     if (stairDirection == 1) {
        !          2204:         if (rogue.depthLevel < DEEPEST_LEVEL) {
        !          2205:             //copyDisplayBuffer(fromBuf, displayBuffer);
        !          2206:             rogue.cursorLoc[0] = rogue.cursorLoc[1] = -1;
        !          2207:             rogue.depthLevel++;
        !          2208:             message("You descend.", false);
        !          2209:             startLevel(rogue.depthLevel - 1, stairDirection);
        !          2210:             if (rogue.depthLevel > rogue.deepestLevel) {
        !          2211:                 rogue.deepestLevel = rogue.depthLevel;
        !          2212:             }
        !          2213:             //copyDisplayBuffer(toBuf, displayBuffer);
        !          2214:             //irisFadeBetweenBuffers(fromBuf, toBuf, mapToWindowX(player.xLoc), mapToWindowY(player.yLoc), 20, false);
        !          2215:         } else if (numberOfMatchingPackItems(AMULET, 0, 0, false)) {
        !          2216:             victory(true);
        !          2217:         } else {
        !          2218:             confirmMessages();
        !          2219:             messageWithColor("the crystal archway repels you with a mysterious force!", &lightBlue, false);
        !          2220:             messageWithColor("(Only the bearer of the Amulet of Yendor may pass.)", &backgroundMessageColor, false);
        !          2221:         }
        !          2222:         succeeded = true;
        !          2223:     } else {
        !          2224:         if (rogue.depthLevel > 1 || numberOfMatchingPackItems(AMULET, 0, 0, false)) {
        !          2225:             rogue.cursorLoc[0] = rogue.cursorLoc[1] = -1;
        !          2226:             rogue.depthLevel--;
        !          2227:             if (rogue.depthLevel == 0) {
        !          2228:                 victory(false);
        !          2229:             } else {
        !          2230:                 //copyDisplayBuffer(fromBuf, displayBuffer);
        !          2231:                 message("You ascend.", false);
        !          2232:                 startLevel(rogue.depthLevel + 1, stairDirection);
        !          2233:                 //copyDisplayBuffer(toBuf, displayBuffer);
        !          2234:                 //irisFadeBetweenBuffers(fromBuf, toBuf, mapToWindowX(player.xLoc), mapToWindowY(player.yLoc), 20, true);
        !          2235:             }
        !          2236:             succeeded = true;
        !          2237:         } else {
        !          2238:             confirmMessages();
        !          2239:             messageWithColor("The dungeon exit is magically sealed!", &lightBlue, false);
        !          2240:             messageWithColor("(Only the bearer of the Amulet of Yendor may pass.)", &backgroundMessageColor, false);
        !          2241:         }
        !          2242:     }
        !          2243:
        !          2244:     if (succeeded) {
        !          2245:         updatePlayerUnderwaterness();
        !          2246:     }
        !          2247:
        !          2248:     return succeeded;
        !          2249: }
        !          2250:
        !          2251: void storeMemories(const short x, const short y) {
        !          2252:     pmap[x][y].rememberedTerrainFlags = terrainFlags(x, y);
        !          2253:     pmap[x][y].rememberedTMFlags = terrainMechFlags(x, y);
        !          2254:     pmap[x][y].rememberedCellFlags = pmap[x][y].flags;
        !          2255:     pmap[x][y].rememberedTerrain = pmap[x][y].layers[highestPriorityLayer(x, y, false)];
        !          2256: }
        !          2257:
        !          2258: void updateFieldOfViewDisplay(boolean updateDancingTerrain, boolean refreshDisplay) {
        !          2259:     short i, j;
        !          2260:     item *theItem;
        !          2261:     char buf[COLS*3], name[COLS*3];
        !          2262:
        !          2263:     assureCosmeticRNG;
        !          2264:
        !          2265:     for (i=0; i<DCOLS; i++) {
        !          2266:         for (j = DROWS-1; j >= 0; j--) {
        !          2267:             if (pmap[i][j].flags & IN_FIELD_OF_VIEW
        !          2268:                 && (max(0, tmap[i][j].light[0])
        !          2269:                     + max(0, tmap[i][j].light[1])
        !          2270:                     + max(0, tmap[i][j].light[2]) > VISIBILITY_THRESHOLD)
        !          2271:                 && !(pmap[i][j].flags & CLAIRVOYANT_DARKENED)) {
        !          2272:
        !          2273:                 pmap[i][j].flags |= VISIBLE;
        !          2274:             }
        !          2275:
        !          2276:             if ((pmap[i][j].flags & VISIBLE) && !(pmap[i][j].flags & WAS_VISIBLE)) { // if the cell became visible this move
        !          2277:                 if (!(pmap[i][j].flags & DISCOVERED) && rogue.automationActive) {
        !          2278:                     if (pmap[i][j].flags & HAS_ITEM) {
        !          2279:                         theItem = itemAtLoc(i, j);
        !          2280:                         if (theItem && (theItem->category & KEY)) {
        !          2281:                             itemName(theItem, name, false, true, NULL);
        !          2282:                             sprintf(buf, "you see %s.", name);
        !          2283:                             messageWithColor(buf, &itemMessageColor, false);
        !          2284:                         }
        !          2285:                     }
        !          2286:                     if (!(pmap[i][j].flags & MAGIC_MAPPED)
        !          2287:                         && cellHasTMFlag(i, j, TM_INTERRUPT_EXPLORATION_WHEN_SEEN)) {
        !          2288:
        !          2289:                         strcpy(name, tileCatalog[pmap[i][j].layers[layerWithTMFlag(i, j, TM_INTERRUPT_EXPLORATION_WHEN_SEEN)]].description);
        !          2290:                         sprintf(buf, "you see %s.", name);
        !          2291:                         messageWithColor(buf, &backgroundMessageColor, false);
        !          2292:                     }
        !          2293:                 }
        !          2294:                 discoverCell(i, j);
        !          2295:                 if (refreshDisplay) {
        !          2296:                     refreshDungeonCell(i, j);
        !          2297:                 }
        !          2298:             } else if (!(pmap[i][j].flags & VISIBLE) && (pmap[i][j].flags & WAS_VISIBLE)) { // if the cell ceased being visible this move
        !          2299:                 storeMemories(i, j);
        !          2300:                 if (refreshDisplay) {
        !          2301:                     refreshDungeonCell(i, j);
        !          2302:                 }
        !          2303:             } else if (!(pmap[i][j].flags & CLAIRVOYANT_VISIBLE) && (pmap[i][j].flags & WAS_CLAIRVOYANT_VISIBLE)) { // ceased being clairvoyantly visible
        !          2304:                 storeMemories(i, j);
        !          2305:                 if (refreshDisplay) {
        !          2306:                     refreshDungeonCell(i, j);
        !          2307:                 }
        !          2308:             } else if (!(pmap[i][j].flags & WAS_CLAIRVOYANT_VISIBLE) && (pmap[i][j].flags & CLAIRVOYANT_VISIBLE)) { // became clairvoyantly visible
        !          2309:                 pmap[i][j].flags &= ~STABLE_MEMORY;
        !          2310:                 if (refreshDisplay) {
        !          2311:                     refreshDungeonCell(i, j);
        !          2312:                 }
        !          2313:             } else if (!(pmap[i][j].flags & TELEPATHIC_VISIBLE) && (pmap[i][j].flags & WAS_TELEPATHIC_VISIBLE)) { // ceased being telepathically visible
        !          2314:                 storeMemories(i, j);
        !          2315:                 if (refreshDisplay) {
        !          2316:                     refreshDungeonCell(i, j);
        !          2317:                 }
        !          2318:             } else if (!(pmap[i][j].flags & WAS_TELEPATHIC_VISIBLE) && (pmap[i][j].flags & TELEPATHIC_VISIBLE)) { // became telepathically visible
        !          2319:                 if (!(pmap[i][j].flags & DISCOVERED)
        !          2320:                     && !cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)) {
        !          2321:                     rogue.xpxpThisTurn++;
        !          2322:                 }
        !          2323:
        !          2324:                 pmap[i][j].flags &= ~STABLE_MEMORY;
        !          2325:                 if (refreshDisplay) {
        !          2326:                     refreshDungeonCell(i, j);
        !          2327:                 }
        !          2328:             } else if (playerCanSeeOrSense(i, j)
        !          2329:                        && (tmap[i][j].light[0] != tmap[i][j].oldLight[0] ||
        !          2330:                            tmap[i][j].light[1] != tmap[i][j].oldLight[1] ||
        !          2331:                            tmap[i][j].light[2] != tmap[i][j].oldLight[2])) { // if the cell's light color changed this move
        !          2332:
        !          2333:                            if (refreshDisplay) {
        !          2334:                                refreshDungeonCell(i, j);
        !          2335:                            }
        !          2336:                        } else if (updateDancingTerrain
        !          2337:                                   && playerCanSee(i, j)
        !          2338:                                   && (!rogue.automationActive || !(rogue.playerTurnNumber % 5))
        !          2339:                                   && ((tileCatalog[pmap[i][j].layers[DUNGEON]].backColor)       && tileCatalog[pmap[i][j].layers[DUNGEON]].backColor->colorDances
        !          2340:                                       || (tileCatalog[pmap[i][j].layers[DUNGEON]].foreColor)    && tileCatalog[pmap[i][j].layers[DUNGEON]].foreColor->colorDances
        !          2341:                                       || (tileCatalog[pmap[i][j].layers[LIQUID]].backColor)     && tileCatalog[pmap[i][j].layers[LIQUID]].backColor->colorDances
        !          2342:                                       || (tileCatalog[pmap[i][j].layers[LIQUID]].foreColor)     && tileCatalog[pmap[i][j].layers[LIQUID]].foreColor->colorDances
        !          2343:                                       || (tileCatalog[pmap[i][j].layers[SURFACE]].backColor)    && tileCatalog[pmap[i][j].layers[SURFACE]].backColor->colorDances
        !          2344:                                       || (tileCatalog[pmap[i][j].layers[SURFACE]].foreColor)    && tileCatalog[pmap[i][j].layers[SURFACE]].foreColor->colorDances
        !          2345:                                       || (tileCatalog[pmap[i][j].layers[GAS]].backColor)        && tileCatalog[pmap[i][j].layers[GAS]].backColor->colorDances
        !          2346:                                       || (tileCatalog[pmap[i][j].layers[GAS]].foreColor)        && tileCatalog[pmap[i][j].layers[GAS]].foreColor->colorDances
        !          2347:                                       || player.status[STATUS_HALLUCINATING])) {
        !          2348:
        !          2349:                                       pmap[i][j].flags &= ~STABLE_MEMORY;
        !          2350:                                       if (refreshDisplay) {
        !          2351:                                           refreshDungeonCell(i, j);
        !          2352:                                       }
        !          2353:                                   }
        !          2354:         }
        !          2355:     }
        !          2356:     restoreRNG;
        !          2357: }
        !          2358:
        !          2359: //         Octants:      //
        !          2360: //          \7|8/        //
        !          2361: //          6\|/1        //
        !          2362: //          --@--        //
        !          2363: //          5/|\2        //
        !          2364: //          /4|3\        //
        !          2365:
        !          2366: void betweenOctant1andN(short *x, short *y, short x0, short y0, short n) {
        !          2367:     short x1 = *x, y1 = *y;
        !          2368:     short dx = x1 - x0, dy = y1 - y0;
        !          2369:     switch (n) {
        !          2370:         case 1:
        !          2371:             return;
        !          2372:         case 2:
        !          2373:             *y = y0 - dy;
        !          2374:             return;
        !          2375:         case 5:
        !          2376:             *x = x0 - dx;
        !          2377:             *y = y0 - dy;
        !          2378:             return;
        !          2379:         case 6:
        !          2380:             *x = x0 - dx;
        !          2381:             return;
        !          2382:         case 8:
        !          2383:             *x = x0 - dy;
        !          2384:             *y = y0 - dx;
        !          2385:             return;
        !          2386:         case 3:
        !          2387:             *x = x0 - dy;
        !          2388:             *y = y0 + dx;
        !          2389:             return;
        !          2390:         case 7:
        !          2391:             *x = x0 + dy;
        !          2392:             *y = y0 - dx;
        !          2393:             return;
        !          2394:         case 4:
        !          2395:             *x = x0 + dy;
        !          2396:             *y = y0 + dx;
        !          2397:             return;
        !          2398:     }
        !          2399: }
        !          2400:
        !          2401: // Returns a boolean grid indicating whether each square is in the field of view of (xLoc, yLoc).
        !          2402: // forbiddenTerrain is the set of terrain flags that will block vision (but the blocking cell itself is
        !          2403: // illuminated); forbiddenFlags is the set of map flags that will block vision.
        !          2404: // If cautiousOnWalls is set, we will not illuminate blocking tiles unless the tile one space closer to the origin
        !          2405: // is visible to the player; this is to prevent lights from illuminating a wall when the player is on the other
        !          2406: // side of the wall.
        !          2407: void getFOVMask(char grid[DCOLS][DROWS], short xLoc, short yLoc, fixpt maxRadius,
        !          2408:                 unsigned long forbiddenTerrain, unsigned long forbiddenFlags, boolean cautiousOnWalls) {
        !          2409:     short i;
        !          2410:
        !          2411:     for (i=1; i<=8; i++) {
        !          2412:         scanOctantFOV(grid, xLoc, yLoc, i, maxRadius, 1, LOS_SLOPE_GRANULARITY * -1, 0,
        !          2413:                       forbiddenTerrain, forbiddenFlags, cautiousOnWalls);
        !          2414:     }
        !          2415: }
        !          2416:
        !          2417: // This is a custom implementation of recursive shadowcasting.
        !          2418: void scanOctantFOV(char grid[DCOLS][DROWS], short xLoc, short yLoc, short octant, fixpt maxRadius,
        !          2419:                    short columnsRightFromOrigin, long startSlope, long endSlope, unsigned long forbiddenTerrain,
        !          2420:                    unsigned long forbiddenFlags, boolean cautiousOnWalls) {
        !          2421:
        !          2422:     if (columnsRightFromOrigin * FP_FACTOR >= maxRadius) return;
        !          2423:
        !          2424:     short i, a, b, iStart, iEnd, x, y, x2, y2; // x and y are temporary variables on which we do the octant transform
        !          2425:     long newStartSlope, newEndSlope;
        !          2426:     boolean cellObstructed;
        !          2427:
        !          2428:     newStartSlope = startSlope;
        !          2429:
        !          2430:     a = ((LOS_SLOPE_GRANULARITY / -2 + 1) + startSlope * columnsRightFromOrigin) / LOS_SLOPE_GRANULARITY;
        !          2431:     b = ((LOS_SLOPE_GRANULARITY / -2 + 1) + endSlope * columnsRightFromOrigin) / LOS_SLOPE_GRANULARITY;
        !          2432:
        !          2433:     iStart = min(a, b);
        !          2434:     iEnd = max(a, b);
        !          2435:
        !          2436:     // restrict vision to a circle of radius maxRadius
        !          2437:     if ((columnsRightFromOrigin*columnsRightFromOrigin + iEnd*iEnd) >= maxRadius*maxRadius / FP_FACTOR / FP_FACTOR) {
        !          2438:         return;
        !          2439:     }
        !          2440:     if ((columnsRightFromOrigin*columnsRightFromOrigin + iStart*iStart) >= maxRadius*maxRadius / FP_FACTOR / FP_FACTOR) {
        !          2441:         iStart = (int) (-1 * fp_sqrt((maxRadius*maxRadius / FP_FACTOR) - (columnsRightFromOrigin*columnsRightFromOrigin * FP_FACTOR)) / FP_FACTOR);
        !          2442:     }
        !          2443:
        !          2444:     x = xLoc + columnsRightFromOrigin;
        !          2445:     y = yLoc + iStart;
        !          2446:     betweenOctant1andN(&x, &y, xLoc, yLoc, octant);
        !          2447:     boolean currentlyLit = coordinatesAreInMap(x, y) && !(cellHasTerrainFlag(x, y, forbiddenTerrain) ||
        !          2448:                                                           (pmap[x][y].flags & forbiddenFlags));
        !          2449:     for (i = iStart; i <= iEnd; i++) {
        !          2450:         x = xLoc + columnsRightFromOrigin;
        !          2451:         y = yLoc + i;
        !          2452:         betweenOctant1andN(&x, &y, xLoc, yLoc, octant);
        !          2453:         if (!coordinatesAreInMap(x, y)) {
        !          2454:             // We're off the map -- here there be memory corruption.
        !          2455:             continue;
        !          2456:         }
        !          2457:         cellObstructed = (cellHasTerrainFlag(x, y, forbiddenTerrain) || (pmap[x][y].flags & forbiddenFlags));
        !          2458:         // if we're cautious on walls and this is a wall:
        !          2459:         if (cautiousOnWalls && cellObstructed) {
        !          2460:             // (x2, y2) is the tile one space closer to the origin from the tile we're on:
        !          2461:             x2 = xLoc + columnsRightFromOrigin - 1;
        !          2462:             y2 = yLoc + i;
        !          2463:             if (i < 0) {
        !          2464:                 y2++;
        !          2465:             } else if (i > 0) {
        !          2466:                 y2--;
        !          2467:             }
        !          2468:             betweenOctant1andN(&x2, &y2, xLoc, yLoc, octant);
        !          2469:
        !          2470:             if (pmap[x2][y2].flags & IN_FIELD_OF_VIEW) {
        !          2471:                 // previous tile is visible, so illuminate
        !          2472:                 grid[x][y] = 1;
        !          2473:             }
        !          2474:         } else {
        !          2475:             // illuminate
        !          2476:             grid[x][y] = 1;
        !          2477:         }
        !          2478:         if (!cellObstructed && !currentlyLit) { // next column slope starts here
        !          2479:             newStartSlope = (long int) ((LOS_SLOPE_GRANULARITY * (i) - LOS_SLOPE_GRANULARITY / 2) / (columnsRightFromOrigin * 2 + 1) * 2);
        !          2480:             currentlyLit = true;
        !          2481:         } else if (cellObstructed && currentlyLit) { // next column slope ends here
        !          2482:             newEndSlope = (long int) ((LOS_SLOPE_GRANULARITY * (i) - LOS_SLOPE_GRANULARITY / 2)
        !          2483:                             / (columnsRightFromOrigin * 2 - 1) * 2);
        !          2484:             if (newStartSlope <= newEndSlope) {
        !          2485:                 // run next column
        !          2486:                 scanOctantFOV(grid, xLoc, yLoc, octant, maxRadius, columnsRightFromOrigin + 1, newStartSlope, newEndSlope,
        !          2487:                               forbiddenTerrain, forbiddenFlags, cautiousOnWalls);
        !          2488:             }
        !          2489:             currentlyLit = false;
        !          2490:         }
        !          2491:     }
        !          2492:     if (currentlyLit) { // got to the bottom of the scan while lit
        !          2493:         newEndSlope = endSlope;
        !          2494:         if (newStartSlope <= newEndSlope) {
        !          2495:             // run next column
        !          2496:             scanOctantFOV(grid, xLoc, yLoc, octant, maxRadius, columnsRightFromOrigin + 1, newStartSlope, newEndSlope,
        !          2497:                           forbiddenTerrain, forbiddenFlags, cautiousOnWalls);
        !          2498:         }
        !          2499:     }
        !          2500: }
        !          2501:
        !          2502: void addScentToCell(short x, short y, short distance) {
        !          2503:     unsigned short value;
        !          2504:     if (!cellHasTerrainFlag(x, y, T_OBSTRUCTS_SCENT) || !cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY)) {
        !          2505:         value = rogue.scentTurnNumber - distance;
        !          2506:         scentMap[x][y] = max(value, (unsigned short) scentMap[x][y]);
        !          2507:     }
        !          2508: }

CVSweb