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

Annotation of brogue-ce/src/brogue/RogueMain.c, Revision 1.1.1.1

1.1       rubenllo    1: /*
                      2:  *  RogueMain.c
                      3:  *  Brogue
                      4:  *
                      5:  *  Created by Brian Walker on 12/26/08.
                      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: #include <time.h>
                     27:
                     28:
                     29: void rogueMain() {
                     30:     previousGameSeed = 0;
                     31:     mainBrogueJunction();
                     32: }
                     33:
                     34: void executeEvent(rogueEvent *theEvent) {
                     35:     rogue.playbackBetweenTurns = false;
                     36:     if (theEvent->eventType == KEYSTROKE) {
                     37:         executeKeystroke(theEvent->param1, theEvent->controlKey, theEvent->shiftKey);
                     38:     } else if (theEvent->eventType == MOUSE_UP
                     39:                || theEvent->eventType == RIGHT_MOUSE_UP) {
                     40:         executeMouseClick(theEvent);
                     41:     }
                     42: }
                     43:
                     44: boolean fileExists(const char *pathname) {
                     45:     FILE *openedFile;
                     46:     openedFile = fopen(pathname, "rb");
                     47:     if (openedFile) {
                     48:         fclose(openedFile);
                     49:         return true;
                     50:     } else {
                     51:         return false;
                     52:     }
                     53: }
                     54:
                     55: // Player specifies a file; if all goes well, put it into path and return true.
                     56: // Otherwise, return false.
                     57: boolean chooseFile(char *path, char *prompt, char *defaultName, char *suffix) {
                     58:
                     59:     if (getInputTextString(path,
                     60:                            prompt,
                     61:                            min(DCOLS-25, BROGUE_FILENAME_MAX - strlen(suffix)),
                     62:                            defaultName,
                     63:                            suffix,
                     64:                            TEXT_INPUT_FILENAME,
                     65:                            false)
                     66:         && path[0] != '\0') {
                     67:
                     68:         strcat(path, suffix);
                     69:         return true;
                     70:     } else {
                     71:         return false;
                     72:     }
                     73: }
                     74:
                     75: // If the file exists, copy it into currentFilePath. (Otherwise return false.)
                     76: // Then, strip off the suffix, replace it with ANNOTATION_SUFFIX,
                     77: // and if that file exists, copy that into annotationPathname. Return true.
                     78: boolean openFile(const char *path) {
                     79:     short i;
                     80:     char buf[BROGUE_FILENAME_MAX];
                     81:     boolean retval;
                     82:
                     83:     if (fileExists(path)) {
                     84:
                     85:         strcpy(currentFilePath, path);
                     86:         annotationPathname[0] = '\0';
                     87:
                     88:         // Clip off the suffix.
                     89:         strcpy(buf, path);
                     90:         for (i = strlen(path); buf[i] != '.' && i > 0; i--) continue;
                     91:         if (buf[i] == '.'
                     92:             && i + strlen(ANNOTATION_SUFFIX) < BROGUE_FILENAME_MAX) {
                     93:
                     94:             buf[i] = '\0'; // Snip!
                     95:             strcat(buf, ANNOTATION_SUFFIX);
                     96:             strcpy(annotationPathname, buf); // Load the annotations file too.
                     97:         }
                     98:         retval = true;
                     99:     } else {
                    100:         retval = false;
                    101:     }
                    102:
                    103:     return retval;
                    104: }
                    105:
                    106: void benchmark() {
                    107:     short i, j, k;
                    108:     const color sparklesauce = {10, 0, 20,  60, 40, 100, 30, true};
                    109:     enum displayGlyph theChar;
                    110:
                    111:     unsigned long initialTime = (unsigned long) time(NULL);
                    112:     for (k=0; k<500; k++) {
                    113:         for (i=0; i<COLS; i++) {
                    114:             for (j=0; j<ROWS; j++) {
                    115:                 theChar = rand_range('!', '~');
                    116:                 plotCharWithColor(theChar, i, j, &sparklesauce, &sparklesauce);
                    117:             }
                    118:         }
                    119:         pauseBrogue(1);
                    120:     }
                    121:     printf("\n\nBenchmark took a total of %lu seconds.", ((unsigned long) time(NULL)) - initialTime);
                    122: }
                    123:
                    124: void welcome() {
                    125:     char buf[DCOLS*3], buf2[DCOLS*3];
                    126:     message("Hello and welcome, adventurer, to the Dungeons of Doom!", false);
                    127:     strcpy(buf, "Retrieve the ");
                    128:     encodeMessageColor(buf, strlen(buf), &itemMessageColor);
                    129:     strcat(buf, "Amulet of Yendor");
                    130:     encodeMessageColor(buf, strlen(buf), &white);
                    131:     sprintf(buf2, " from the %ith floor and escape with it!", AMULET_LEVEL);
                    132:     strcat(buf, buf2);
                    133:     message(buf, false);
                    134:     if (KEYBOARD_LABELS) {
                    135:         messageWithColor("Press <?> for help at any time.", &backgroundMessageColor, false);
                    136:     }
                    137:     flavorMessage("The doors to the dungeon slam shut behind you.");
                    138: }
                    139:
                    140: // Seed is used as the dungeon seed unless it's zero, in which case generate a new one.
                    141: // Either way, previousGameSeed is set to the seed we use.
                    142: // None of this seed stuff is applicable if we're playing a recording.
                    143: void initializeRogue(unsigned long seed) {
                    144:     short i, j, k;
                    145:     item *theItem;
                    146:     boolean playingback, playbackFF, playbackPaused, wizard, displayAggroRangeMode;
                    147:     boolean trueColorMode;
                    148:     short oldRNG;
                    149:
                    150:     playingback = rogue.playbackMode; // the only animals that need to go on the ark
                    151:     playbackPaused = rogue.playbackPaused;
                    152:     playbackFF = rogue.playbackFastForward;
                    153:     wizard = rogue.wizard;
                    154:     displayAggroRangeMode = rogue.displayAggroRangeMode;
                    155:     trueColorMode = rogue.trueColorMode;
                    156:     memset((void *) &rogue, 0, sizeof( playerCharacter )); // the flood
                    157:     rogue.playbackMode = playingback;
                    158:     rogue.playbackPaused = playbackPaused;
                    159:     rogue.playbackFastForward = playbackFF;
                    160:     rogue.wizard = wizard;
                    161:     rogue.displayAggroRangeMode = displayAggroRangeMode;
                    162:     rogue.trueColorMode = trueColorMode;
                    163:
                    164:     rogue.gameHasEnded = false;
                    165:     rogue.gameInProgress = true;
                    166:     rogue.highScoreSaved = false;
                    167:     rogue.cautiousMode = false;
                    168:     rogue.milliseconds = 0;
                    169:
                    170:     rogue.RNG = RNG_SUBSTANTIVE;
                    171:     if (!rogue.playbackMode) {
                    172:         rogue.seed = seedRandomGenerator(seed);
                    173:         previousGameSeed = rogue.seed;
                    174:     }
                    175:
                    176:     //benchmark();
                    177:
                    178:     initRecording();
                    179:
                    180:     levels = malloc(sizeof(levelData) * (DEEPEST_LEVEL+1));
                    181:     levels[0].upStairsLoc[0] = (DCOLS - 1) / 2 - 1;
                    182:     levels[0].upStairsLoc[1] = DROWS - 2;
                    183:
                    184:     // reset enchant and gain strength frequencies
                    185:     rogue.lifePotionFrequency = 0;
                    186:     rogue.strengthPotionFrequency = 40;
                    187:     rogue.enchantScrollFrequency = 60;
                    188:
                    189:     // all DF messages are eligible for display
                    190:     resetDFMessageEligibility();
                    191:
                    192:     // initialize the levels list
                    193:     for (i=0; i<DEEPEST_LEVEL+1; i++) {
                    194:         levels[i].levelSeed = (unsigned long) rand_range(0, 9999);
                    195:         levels[i].levelSeed += (unsigned long) 10000 * rand_range(0, 9999);
                    196:         levels[i].monsters = NULL;
                    197:         levels[i].dormantMonsters = NULL;
                    198:         levels[i].items = NULL;
                    199:         levels[i].scentMap = NULL;
                    200:         levels[i].visited = false;
                    201:         levels[i].playerExitedVia[0] = 0;
                    202:         levels[i].playerExitedVia[1] = 0;
                    203:         do {
                    204:             levels[i].downStairsLoc[0] = rand_range(1, DCOLS - 2);
                    205:             levels[i].downStairsLoc[1] = rand_range(1, DROWS - 2);
                    206:         } while (distanceBetween(levels[i].upStairsLoc[0], levels[i].upStairsLoc[1],
                    207:                                  levels[i].downStairsLoc[0], levels[i].downStairsLoc[1]) < DCOLS / 3);
                    208:         if (i < DEEPEST_LEVEL) {
                    209:             levels[i+1].upStairsLoc[0] = levels[i].downStairsLoc[0];
                    210:             levels[i+1].upStairsLoc[1] = levels[i].downStairsLoc[1];
                    211:         }
                    212:     }
                    213:
                    214:     // initialize the waypoints list
                    215:     for (i=0; i<MAX_WAYPOINT_COUNT; i++) {
                    216:         rogue.wpDistance[i] = allocGrid();
                    217:         fillGrid(rogue.wpDistance[i], 0);
                    218:     }
                    219:
                    220:     rogue.rewardRoomsGenerated = 0;
                    221:
                    222:     // pre-shuffle the random terrain colors
                    223:     oldRNG = rogue.RNG;
                    224:     rogue.RNG = RNG_COSMETIC;
                    225:     //assureCosmeticRNG;
                    226:     for (i=0; i<DCOLS; i++) {
                    227:         for( j=0; j<DROWS; j++ ) {
                    228:             for (k=0; k<8; k++) {
                    229:                 terrainRandomValues[i][j][k] = rand_range(0, 1000);
                    230:             }
                    231:         }
                    232:     }
                    233:     restoreRNG;
                    234:
                    235:     zeroOutGrid(displayDetail);
                    236:
                    237:     for (i=0; i<NUMBER_MONSTER_KINDS; i++) {
                    238:         monsterCatalog[i].monsterID = i;
                    239:     }
                    240:
                    241:     shuffleFlavors();
                    242:
                    243:     for (i = 0; i < FEAT_COUNT; i++) {
                    244:         rogue.featRecord[i] = featTable[i].initialValue;
                    245:     }
                    246:
                    247:     deleteMessages();
                    248:     for (i = 0; i < MESSAGE_ARCHIVE_LINES; i++) { // Clear the message archive.
                    249:         messageArchive[i][0] = '\0';
                    250:     }
                    251:     messageArchivePosition = 0;
                    252:
                    253:     // Seed the stacks.
                    254:     floorItems = (item *) malloc(sizeof(item));
                    255:     memset(floorItems, '\0', sizeof(item));
                    256:     floorItems->nextItem = NULL;
                    257:
                    258:     packItems = (item *) malloc(sizeof(item));
                    259:     memset(packItems, '\0', sizeof(item));
                    260:     packItems->nextItem = NULL;
                    261:
                    262:     monsterItemsHopper = (item *) malloc(sizeof(item));
                    263:     memset(monsterItemsHopper, '\0', sizeof(item));
                    264:     monsterItemsHopper->nextItem = NULL;
                    265:
                    266:     for (i = 0; i < MAX_ITEMS_IN_MONSTER_ITEMS_HOPPER; i++) {
                    267:         theItem = generateItem(ALL_ITEMS & ~FOOD, -1); // Monsters can't carry food: the food clock cannot be cheated!
                    268:         theItem->nextItem = monsterItemsHopper->nextItem;
                    269:         monsterItemsHopper->nextItem = theItem;
                    270:     }
                    271:
                    272:     monsters = (creature *) malloc(sizeof(creature));
                    273:     memset(monsters, '\0', sizeof(creature));
                    274:     monsters->nextCreature = NULL;
                    275:
                    276:     dormantMonsters = (creature *) malloc(sizeof(creature));
                    277:     memset(dormantMonsters, '\0', sizeof(creature));
                    278:     dormantMonsters->nextCreature = NULL;
                    279:
                    280:     graveyard = (creature *) malloc(sizeof(creature));
                    281:     memset(graveyard, '\0', sizeof(creature));
                    282:     graveyard->nextCreature = NULL;
                    283:
                    284:     purgatory = (creature *) malloc(sizeof(creature));
                    285:     memset(purgatory, '\0', sizeof(creature));
                    286:     purgatory->nextCreature = NULL;
                    287:
                    288:     scentMap            = NULL;
                    289:     safetyMap           = allocGrid();
                    290:     allySafetyMap       = allocGrid();
                    291:     chokeMap            = allocGrid();
                    292:
                    293:     rogue.mapToSafeTerrain = allocGrid();
                    294:
                    295:     // Zero out the dynamic grids, as an essential safeguard against OOSes:
                    296:     fillGrid(safetyMap, 0);
                    297:     fillGrid(allySafetyMap, 0);
                    298:     fillGrid(chokeMap, 0);
                    299:     fillGrid(rogue.mapToSafeTerrain, 0);
                    300:
                    301:     // initialize the player
                    302:
                    303:     memset(&player, '\0', sizeof(creature));
                    304:     player.info = monsterCatalog[0];
                    305:     initializeGender(&player);
                    306:     player.movementSpeed = player.info.movementSpeed;
                    307:     player.attackSpeed = player.info.attackSpeed;
                    308:     clearStatus(&player);
                    309:     player.carriedItem = NULL;
                    310:     player.status[STATUS_NUTRITION] = player.maxStatus[STATUS_NUTRITION] = STOMACH_SIZE;
                    311:     player.currentHP = player.info.maxHP;
                    312:     player.creatureState = MONSTER_ALLY;
                    313:     player.ticksUntilTurn = 0;
                    314:     player.mutationIndex = -1;
                    315:
                    316:     rogue.depthLevel = 1;
                    317:     rogue.deepestLevel = 1;
                    318:     rogue.scentTurnNumber = 1000;
                    319:     rogue.playerTurnNumber = 0;
                    320:     rogue.absoluteTurnNumber = 0;
                    321:     rogue.previousPoisonPercent = 0;
                    322:     rogue.foodSpawned = 0;
                    323:     rogue.lifePotionsSpawned = 0;
                    324:     rogue.gold = 0;
                    325:     rogue.goldGenerated = 0;
                    326:     rogue.disturbed = false;
                    327:     rogue.autoPlayingLevel = false;
                    328:     rogue.automationActive = false;
                    329:     rogue.justRested = false;
                    330:     rogue.justSearched = false;
                    331:     rogue.easyMode = false;
                    332:     rogue.inWater = false;
                    333:     rogue.creaturesWillFlashThisTurn = false;
                    334:     rogue.updatedSafetyMapThisTurn = false;
                    335:     rogue.updatedAllySafetyMapThisTurn = false;
                    336:     rogue.updatedMapToSafeTerrainThisTurn = false;
                    337:     rogue.updatedMapToShoreThisTurn = false;
                    338:     rogue.strength = 12;
                    339:     rogue.weapon = NULL;
                    340:     rogue.armor = NULL;
                    341:     rogue.ringLeft = NULL;
                    342:     rogue.ringRight = NULL;
                    343:     rogue.monsterSpawnFuse = rand_range(125, 175);
                    344:     rogue.ticksTillUpdateEnvironment = 100;
                    345:     rogue.mapToShore = NULL;
                    346:     rogue.cursorLoc[0] = rogue.cursorLoc[1] = -1;
                    347:     rogue.xpxpThisTurn = 0;
                    348:
                    349:     rogue.yendorWarden = NULL;
                    350:
                    351:     rogue.flares = NULL;
                    352:     rogue.flareCount = rogue.flareCapacity = 0;
                    353:
                    354:     rogue.minersLight = lightCatalog[MINERS_LIGHT];
                    355:
                    356:     rogue.clairvoyance = rogue.regenerationBonus
                    357:     = rogue.stealthBonus = rogue.transference = rogue.wisdomBonus = rogue.reaping = 0;
                    358:     rogue.lightMultiplier = 1;
                    359:
                    360:     theItem = generateItem(FOOD, RATION);
                    361:     theItem = addItemToPack(theItem);
                    362:
                    363:     theItem = generateItem(WEAPON, DAGGER);
                    364:     theItem->enchant1 = theItem->enchant2 = 0;
                    365:     theItem->flags &= ~(ITEM_CURSED | ITEM_RUNIC);
                    366:     identify(theItem);
                    367:     theItem = addItemToPack(theItem);
                    368:     equipItem(theItem, false);
                    369:
                    370:     theItem = generateItem(WEAPON, DART);
                    371:     theItem->enchant1 = theItem->enchant2 = 0;
                    372:     theItem->quantity = 15;
                    373:     theItem->flags &= ~(ITEM_CURSED | ITEM_RUNIC);
                    374:     identify(theItem);
                    375:     theItem = addItemToPack(theItem);
                    376:
                    377:     theItem = generateItem(ARMOR, LEATHER_ARMOR);
                    378:     theItem->enchant1 = 0;
                    379:     theItem->flags &= ~(ITEM_CURSED | ITEM_RUNIC);
                    380:     identify(theItem);
                    381:     theItem = addItemToPack(theItem);
                    382:     equipItem(theItem, false);
                    383:     player.status[STATUS_DONNING] = 0;
                    384:
                    385:     recalculateEquipmentBonuses();
                    386:
                    387:     DEBUG {
                    388:         theItem = generateItem(RING, RING_CLAIRVOYANCE);
                    389:         theItem->enchant1 = max(DROWS, DCOLS);
                    390:         theItem->flags &= ~ITEM_CURSED;
                    391:         identify(theItem);
                    392:         theItem = addItemToPack(theItem);
                    393:
                    394:         theItem = generateItem(RING, RING_AWARENESS);
                    395:         theItem->enchant1 = 30;
                    396:         theItem->flags &= ~ITEM_CURSED;
                    397:         identify(theItem);
                    398:         theItem = addItemToPack(theItem);
                    399:
                    400:         theItem = generateItem(WEAPON, DAGGER);
                    401:         theItem->enchant1 = 50;
                    402:         theItem->enchant2 = W_QUIETUS;
                    403:         theItem->flags &= ~(ITEM_CURSED);
                    404:         theItem->flags |= (ITEM_PROTECTED | ITEM_RUNIC | ITEM_RUNIC_HINTED);
                    405:         theItem->damage.lowerBound = theItem->damage.upperBound = 25;
                    406:         identify(theItem);
                    407:         theItem = addItemToPack(theItem);
                    408:
                    409:         theItem = generateItem(ARMOR, LEATHER_ARMOR);
                    410:         theItem->enchant1 = 50;
                    411:         theItem->enchant2 = A_REFLECTION;
                    412:         theItem->flags &= ~(ITEM_CURSED | ITEM_RUNIC_HINTED);
                    413:         theItem->flags |= (ITEM_PROTECTED | ITEM_RUNIC);
                    414:         identify(theItem);
                    415:         theItem = addItemToPack(theItem);
                    416:
                    417:         theItem = generateItem(STAFF, STAFF_FIRE);
                    418:         theItem->enchant1 = 10;
                    419:         theItem->charges = 300;
                    420:         identify(theItem);
                    421:         theItem = addItemToPack(theItem);
                    422:
                    423:         theItem = generateItem(STAFF, STAFF_LIGHTNING);
                    424:         theItem->enchant1 = 10;
                    425:         theItem->charges = 300;
                    426:         identify(theItem);
                    427:         theItem = addItemToPack(theItem);
                    428:
                    429:         theItem = generateItem(STAFF, STAFF_TUNNELING);
                    430:         theItem->enchant1 = 10;
                    431:         theItem->charges = 3000;
                    432:         identify(theItem);
                    433:         theItem = addItemToPack(theItem);
                    434:
                    435:         theItem = generateItem(STAFF, STAFF_OBSTRUCTION);
                    436:         theItem->enchant1 = 10;
                    437:         theItem->charges = 300;
                    438:         identify(theItem);
                    439:         theItem = addItemToPack(theItem);
                    440:
                    441:         theItem = generateItem(STAFF, STAFF_ENTRANCEMENT);
                    442:         theItem->enchant1 = 10;
                    443:         theItem->charges = 300;
                    444:         identify(theItem);
                    445:         theItem = addItemToPack(theItem);
                    446:
                    447:         theItem = generateItem(WAND, WAND_BECKONING);
                    448:         theItem->charges = 3000;
                    449:         identify(theItem);
                    450:         theItem = addItemToPack(theItem);
                    451:
                    452:         theItem = generateItem(WAND, WAND_DOMINATION);
                    453:         theItem->charges = 300;
                    454:         identify(theItem);
                    455:         theItem = addItemToPack(theItem);
                    456:
                    457:         theItem = generateItem(WAND, WAND_PLENTY);
                    458:         theItem->charges = 300;
                    459:         identify(theItem);
                    460:         theItem = addItemToPack(theItem);
                    461:
                    462:         theItem = generateItem(WAND, WAND_NEGATION);
                    463:         theItem->charges = 300;
                    464:         identify(theItem);
                    465:         theItem = addItemToPack(theItem);
                    466:
                    467: //      short i;
                    468: //      for (i=0; i < NUMBER_CHARM_KINDS && i < 4; i++) {
                    469: //          theItem = generateItem(CHARM, i);
                    470: //          theItem = addItemToPack(theItem);
                    471: //      }
                    472:     }
                    473:     blackOutScreen();
                    474:     welcome();
                    475: }
                    476:
                    477: // call this once per level to set all the dynamic colors as a function of depth
                    478: void updateColors() {
                    479:     short i;
                    480:
                    481:     for (i=0; i<NUMBER_DYNAMIC_COLORS; i++) {
                    482:         *(dynamicColors[i][0]) = *(dynamicColors[i][1]);
                    483:         applyColorAverage(dynamicColors[i][0], dynamicColors[i][2], min(100, max(0, rogue.depthLevel * 100 / AMULET_LEVEL)));
                    484:     }
                    485: }
                    486:
                    487: void startLevel(short oldLevelNumber, short stairDirection) {
                    488:     unsigned long oldSeed;
                    489:     item *theItem;
                    490:     short loc[2], i, j, x, y, px, py, flying, dir;
                    491:     boolean placedPlayer;
                    492:     creature *monst;
                    493:     enum dungeonLayers layer;
                    494:     unsigned long timeAway;
                    495:     short **mapToStairs;
                    496:     short **mapToPit;
                    497:     boolean connectingStairsDiscovered;
                    498:
                    499:     if (oldLevelNumber == DEEPEST_LEVEL && stairDirection != -1) {
                    500:         return;
                    501:     }
                    502:
                    503:     synchronizePlayerTimeState();
                    504:
                    505:     rogue.updatedSafetyMapThisTurn          = false;
                    506:     rogue.updatedAllySafetyMapThisTurn      = false;
                    507:     rogue.updatedMapToSafeTerrainThisTurn   = false;
                    508:
                    509:     rogue.cursorLoc[0] = -1;
                    510:     rogue.cursorLoc[1] = -1;
                    511:     rogue.lastTarget = NULL;
                    512:
                    513:     connectingStairsDiscovered = (pmap[rogue.downLoc[0]][rogue.downLoc[1]].flags & (DISCOVERED | MAGIC_MAPPED) ? true : false);
                    514:     if (stairDirection == 0) { // fallen
                    515:         levels[oldLevelNumber-1].playerExitedVia[0] = player.xLoc;
                    516:         levels[oldLevelNumber-1].playerExitedVia[1] = player.yLoc;
                    517:     }
                    518:
                    519:     if (oldLevelNumber != rogue.depthLevel) {
                    520:         px = player.xLoc;
                    521:         py = player.yLoc;
                    522:         if (cellHasTerrainFlag(player.xLoc, player.yLoc, T_AUTO_DESCENT)) {
                    523:             for (i=0; i<8; i++) {
                    524:                 if (!cellHasTerrainFlag(player.xLoc+nbDirs[i][0], player.yLoc+nbDirs[i][1], (T_PATHING_BLOCKER))) {
                    525:                     px = player.xLoc+nbDirs[i][0];
                    526:                     py = player.yLoc+nbDirs[i][1];
                    527:                     break;
                    528:                 }
                    529:             }
                    530:         }
                    531:         mapToStairs = allocGrid();
                    532:         fillGrid(mapToStairs, 0);
                    533:         for (flying = 0; flying <= 1; flying++) {
                    534:             fillGrid(mapToStairs, 0);
                    535:             calculateDistances(mapToStairs, px, py, (flying ? T_OBSTRUCTS_PASSABILITY : T_PATHING_BLOCKER) | T_SACRED, NULL, true, true);
                    536:             for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
                    537:                 x = monst->xLoc;
                    538:                 y = monst->yLoc;
                    539:                 if (((monst->creatureState == MONSTER_TRACKING_SCENT && (stairDirection != 0 || monst->status[STATUS_LEVITATING]))
                    540:                      || monst->creatureState == MONSTER_ALLY || monst == rogue.yendorWarden)
                    541:                     && (stairDirection != 0 || monst->currentHP > 10 || monst->status[STATUS_LEVITATING])
                    542:                     && ((flying != 0) == ((monst->status[STATUS_LEVITATING] != 0)
                    543:                                           || cellHasTerrainFlag(x, y, T_PATHING_BLOCKER)
                    544:                                           || cellHasTerrainFlag(px, py, T_AUTO_DESCENT)))
                    545:                     && !(monst->bookkeepingFlags & MB_CAPTIVE)
                    546:                     && !(monst->info.flags & (MONST_WILL_NOT_USE_STAIRS | MONST_RESTRICTED_TO_LIQUID))
                    547:                     && !(cellHasTerrainFlag(x, y, T_OBSTRUCTS_PASSABILITY))
                    548:                     && !monst->status[STATUS_ENTRANCED]
                    549:                     && !monst->status[STATUS_PARALYZED]
                    550:                     && (mapToStairs[monst->xLoc][monst->yLoc] < 30000 || monst->creatureState == MONSTER_ALLY || monst == rogue.yendorWarden)) {
                    551:
                    552:                     monst->status[STATUS_ENTERS_LEVEL_IN] = clamp(mapToStairs[monst->xLoc][monst->yLoc] * monst->movementSpeed / 100 + 1, 1, 150);
                    553:                     switch (stairDirection) {
                    554:                         case 1:
                    555:                             monst->bookkeepingFlags |= MB_APPROACHING_DOWNSTAIRS;
                    556:                             break;
                    557:                         case -1:
                    558:                             monst->bookkeepingFlags |= MB_APPROACHING_UPSTAIRS;
                    559:                             break;
                    560:                         case 0:
                    561:                             monst->bookkeepingFlags |= MB_APPROACHING_PIT;
                    562:                             break;
                    563:                         default:
                    564:                             break;
                    565:                     }
                    566:                 }
                    567:             }
                    568:         }
                    569:         freeGrid(mapToStairs);
                    570:     }
                    571:
                    572:     for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
                    573:         if (monst->mapToMe) {
                    574:             freeGrid(monst->mapToMe);
                    575:             monst->mapToMe = NULL;
                    576:         }
                    577:         if (rogue.patchVersion < 3 && monst->safetyMap) {
                    578:             freeGrid(monst->safetyMap);
                    579:             monst->safetyMap = NULL;
                    580:         }
                    581:     }
                    582:     levels[oldLevelNumber-1].monsters = monsters->nextCreature;
                    583:     levels[oldLevelNumber-1].dormantMonsters = dormantMonsters->nextCreature;
                    584:     levels[oldLevelNumber-1].items = floorItems->nextItem;
                    585:
                    586:     for (i=0; i<DCOLS; i++) {
                    587:         for (j=0; j<DROWS; j++) {
                    588:             if (pmap[i][j].flags & (rogue.patchVersion >= 3 ? ANY_KIND_OF_VISIBLE : VISIBLE)) {
                    589:                 // Remember visible cells upon exiting.
                    590:                 storeMemories(i, j);
                    591:             }
                    592:             for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
                    593:                 levels[oldLevelNumber - 1].mapStorage[i][j].layers[layer] = pmap[i][j].layers[layer];
                    594:             }
                    595:             levels[oldLevelNumber - 1].mapStorage[i][j].volume = pmap[i][j].volume;
                    596:             levels[oldLevelNumber - 1].mapStorage[i][j].flags = (pmap[i][j].flags & (rogue.patchVersion < 3 ? (PERMANENT_TILE_FLAGS & ~HAS_MONSTER) : PERMANENT_TILE_FLAGS));
                    597:             levels[oldLevelNumber - 1].mapStorage[i][j].machineNumber = pmap[i][j].machineNumber;
                    598:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedAppearance = pmap[i][j].rememberedAppearance;
                    599:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedItemCategory = pmap[i][j].rememberedItemCategory;
                    600:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedItemKind = pmap[i][j].rememberedItemKind;
                    601:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedItemQuantity = pmap[i][j].rememberedItemQuantity;
                    602:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedItemOriginDepth = pmap[i][j].rememberedItemOriginDepth;
                    603:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedTerrain = pmap[i][j].rememberedTerrain;
                    604:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedCellFlags = pmap[i][j].rememberedCellFlags;
                    605:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedTerrainFlags = pmap[i][j].rememberedTerrainFlags;
                    606:             levels[oldLevelNumber - 1].mapStorage[i][j].rememberedTMFlags = pmap[i][j].rememberedTMFlags;
                    607:         }
                    608:     }
                    609:
                    610:     levels[oldLevelNumber - 1].awaySince = rogue.absoluteTurnNumber;
                    611:
                    612:     //  Prepare the new level
                    613:     rogue.minersLightRadius = (DCOLS - 1) * FP_FACTOR;
                    614:     for (i = 0; i < rogue.depthLevel; i++) {
                    615:         rogue.minersLightRadius = rogue.minersLightRadius * 85 / 100;
                    616:     }
                    617:     rogue.minersLightRadius += FP_FACTOR * 225 / 100;
                    618:     updateColors();
                    619:     updateRingBonuses(); // also updates miner's light
                    620:
                    621:     if (!levels[rogue.depthLevel - 1].visited) { // level has not already been visited
                    622:         levels[rogue.depthLevel - 1].scentMap = allocGrid();
                    623:         scentMap = levels[rogue.depthLevel - 1].scentMap;
                    624:         fillGrid(levels[rogue.depthLevel - 1].scentMap, 0);
                    625:         // generate new level
                    626:         oldSeed = (unsigned long) rand_range(0, 9999);
                    627:         oldSeed += (unsigned long) 10000 * rand_range(0, 9999);
                    628:         seedRandomGenerator(levels[rogue.depthLevel - 1].levelSeed);
                    629:
                    630:         // Load up next level's monsters and items, since one might have fallen from above.
                    631:         monsters->nextCreature          = levels[rogue.depthLevel-1].monsters;
                    632:         dormantMonsters->nextCreature   = levels[rogue.depthLevel-1].dormantMonsters;
                    633:         floorItems->nextItem            = levels[rogue.depthLevel-1].items;
                    634:
                    635:         levels[rogue.depthLevel-1].monsters = NULL;
                    636:         levels[rogue.depthLevel-1].dormantMonsters = NULL;
                    637:         levels[rogue.depthLevel-1].items = NULL;
                    638:
                    639:         digDungeon();
                    640:         initializeLevel();
                    641:         setUpWaypoints();
                    642:
                    643:         shuffleTerrainColors(100, false);
                    644:
                    645:         // If we somehow failed to generate the amulet altar,
                    646:         // just toss an amulet in there somewhere.
                    647:         // It'll be fiiine!
                    648:         if (rogue.depthLevel == AMULET_LEVEL
                    649:             && !numberOfMatchingPackItems(AMULET, 0, 0, false)
                    650:             && levels[rogue.depthLevel-1].visited == false) {
                    651:
                    652:             for (theItem = floorItems->nextItem; theItem != NULL; theItem = theItem->nextItem) {
                    653:                 if (theItem->category & AMULET) {
                    654:                     break;
                    655:                 }
                    656:             }
                    657:             for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
                    658:                 if (monst->carriedItem
                    659:                     && (monst->carriedItem->category & AMULET)) {
                    660:
                    661:                     theItem = monst->carriedItem;
                    662:                     break;
                    663:                 }
                    664:             }
                    665:             if (!theItem) {
                    666:                 placeItem(generateItem(AMULET, 0), 0, 0);
                    667:             }
                    668:         }
                    669:         seedRandomGenerator(oldSeed);
                    670:
                    671:         //logLevel();
                    672:
                    673:         // Simulate 50 turns so the level is broken in (swamp gas accumulating, brimstone percolating, etc.).
                    674:         timeAway = 50;
                    675:
                    676:     } else { // level has already been visited
                    677:
                    678:         // restore level
                    679:         scentMap = levels[rogue.depthLevel - 1].scentMap;
                    680:         timeAway = clamp(0, rogue.absoluteTurnNumber - levels[rogue.depthLevel - 1].awaySince, 30000);
                    681:
                    682:         for (i=0; i<DCOLS; i++) {
                    683:             for (j=0; j<DROWS; j++) {
                    684:                 for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
                    685:                     pmap[i][j].layers[layer] = levels[rogue.depthLevel - 1].mapStorage[i][j].layers[layer];
                    686:                 }
                    687:                 pmap[i][j].volume = levels[rogue.depthLevel - 1].mapStorage[i][j].volume;
                    688:                 pmap[i][j].flags = (levels[rogue.depthLevel - 1].mapStorage[i][j].flags & (rogue.patchVersion < 3 ? (PERMANENT_TILE_FLAGS & ~HAS_MONSTER) : PERMANENT_TILE_FLAGS));
                    689:                 pmap[i][j].machineNumber = levels[rogue.depthLevel - 1].mapStorage[i][j].machineNumber;
                    690:                 pmap[i][j].rememberedAppearance = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedAppearance;
                    691:                 pmap[i][j].rememberedItemCategory = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedItemCategory;
                    692:                 pmap[i][j].rememberedItemKind = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedItemKind;
                    693:                 pmap[i][j].rememberedItemQuantity = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedItemQuantity;
                    694:                 pmap[i][j].rememberedItemOriginDepth = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedItemOriginDepth;
                    695:                 pmap[i][j].rememberedTerrain = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedTerrain;
                    696:                 pmap[i][j].rememberedCellFlags = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedCellFlags;
                    697:                 pmap[i][j].rememberedTerrainFlags = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedTerrainFlags;
                    698:                 pmap[i][j].rememberedTMFlags = levels[rogue.depthLevel - 1].mapStorage[i][j].rememberedTMFlags;
                    699:             }
                    700:         }
                    701:
                    702:         setUpWaypoints();
                    703:
                    704:         rogue.downLoc[0]    = levels[rogue.depthLevel - 1].downStairsLoc[0];
                    705:         rogue.downLoc[1]    = levels[rogue.depthLevel - 1].downStairsLoc[1];
                    706:         rogue.upLoc[0]      = levels[rogue.depthLevel - 1].upStairsLoc[0];
                    707:         rogue.upLoc[1]      = levels[rogue.depthLevel - 1].upStairsLoc[1];
                    708:
                    709:         monsters->nextCreature = levels[rogue.depthLevel - 1].monsters;
                    710:         dormantMonsters->nextCreature = levels[rogue.depthLevel - 1].dormantMonsters;
                    711:         floorItems->nextItem = levels[rogue.depthLevel - 1].items;
                    712:
                    713:         levels[rogue.depthLevel-1].monsters = NULL;
                    714:         levels[rogue.depthLevel-1].dormantMonsters = NULL;
                    715:         levels[rogue.depthLevel-1].items = NULL;
                    716:
                    717:         for (theItem = floorItems->nextItem; theItem != NULL; theItem = theItem->nextItem) {
                    718:             restoreItem(theItem);
                    719:         }
                    720:
                    721:         if (rogue.patchVersion < 3) {
                    722:             mapToStairs = allocGrid();
                    723:             mapToPit = allocGrid();
                    724:             fillGrid(mapToStairs, 0);
                    725:             fillGrid(mapToPit, 0);
                    726:             calculateDistances(mapToStairs, player.xLoc, player.yLoc, T_PATHING_BLOCKER, NULL, true, true);
                    727:             calculateDistances(mapToPit, levels[rogue.depthLevel-1].playerExitedVia[0],
                    728:                                levels[rogue.depthLevel-1].playerExitedVia[0], T_PATHING_BLOCKER, NULL, true, true);
                    729:             for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
                    730:                 restoreMonster(monst, mapToStairs, mapToPit);
                    731:             }
                    732:             freeGrid(mapToStairs);
                    733:             freeGrid(mapToPit);
                    734:         }
                    735:     }
                    736:
                    737:     // Simulate the environment!
                    738:     // First bury the player in limbo while we run the simulation,
                    739:     // so that any harmful terrain doesn't affect her during the process.
                    740:     px = player.xLoc;
                    741:     py = player.yLoc;
                    742:     player.xLoc = player.yLoc = 0;
                    743:     for (i = 0; i < 100 && i < (short) timeAway; i++) {
                    744:         updateEnvironment();
                    745:     }
                    746:     player.xLoc = px;
                    747:     player.yLoc = py;
                    748:
                    749:     if (!levels[rogue.depthLevel-1].visited) {
                    750:         levels[rogue.depthLevel-1].visited = true;
                    751:         if (rogue.depthLevel == AMULET_LEVEL) {
                    752:             messageWithColor("An alien energy permeates the area. The Amulet of Yendor must be nearby!", &itemMessageColor, false);
                    753:         } else if (rogue.depthLevel == DEEPEST_LEVEL) {
                    754:             messageWithColor("An overwhelming sense of peace and tranquility settles upon you.", &lightBlue, false);
                    755:         }
                    756:     }
                    757:
                    758:     // Position the player.
                    759:     if (stairDirection == 0) { // fell into the level
                    760:
                    761:         getQualifyingLocNear(loc, player.xLoc, player.yLoc, true, 0,
                    762:                              (T_PATHING_BLOCKER),
                    763:                              (HAS_MONSTER | HAS_ITEM | HAS_STAIRS | IS_IN_MACHINE), false, false);
                    764:     } else {
                    765:         if (stairDirection == 1) { // heading downward
                    766:             player.xLoc = rogue.upLoc[0];
                    767:             player.yLoc = rogue.upLoc[1];
                    768:         } else if (stairDirection == -1) { // heading upward
                    769:             player.xLoc = rogue.downLoc[0];
                    770:             player.yLoc = rogue.downLoc[1];
                    771:         }
                    772:
                    773:         placedPlayer = false;
                    774:         for (dir=0; dir<4 && !placedPlayer; dir++) {
                    775:             loc[0] = player.xLoc + nbDirs[dir][0];
                    776:             loc[1] = player.yLoc + nbDirs[dir][1];
                    777:             if (!cellHasTerrainFlag(loc[0], loc[1], T_PATHING_BLOCKER)
                    778:                 && !(pmap[loc[0]][loc[1]].flags & (HAS_MONSTER | HAS_ITEM | HAS_STAIRS | IS_IN_MACHINE))) {
                    779:                 placedPlayer = true;
                    780:             }
                    781:         }
                    782:         if (!placedPlayer) {
                    783:             getQualifyingPathLocNear(&loc[0], &loc[1],
                    784:                                      player.xLoc, player.yLoc,
                    785:                                      true,
                    786:                                      T_DIVIDES_LEVEL, 0,
                    787:                                      T_PATHING_BLOCKER, (HAS_MONSTER | HAS_ITEM | HAS_STAIRS | IS_IN_MACHINE),
                    788:                                      false);
                    789:         }
                    790:     }
                    791:     player.xLoc = loc[0];
                    792:     player.yLoc = loc[1];
                    793:
                    794:     pmap[player.xLoc][player.yLoc].flags |= HAS_PLAYER;
                    795:
                    796:     if (connectingStairsDiscovered) {
                    797:         for (i = rogue.upLoc[0]-1; i <= rogue.upLoc[0] + 1; i++) {
                    798:             for (j = rogue.upLoc[1]-1; j <= rogue.upLoc[1] + 1; j++) {
                    799:                 if (coordinatesAreInMap(i, j)) {
                    800:                     discoverCell(i, j);
                    801:                 }
                    802:             }
                    803:         }
                    804:     }
                    805:     if (cellHasTerrainFlag(player.xLoc, player.yLoc, T_IS_DEEP_WATER) && !player.status[STATUS_LEVITATING]
                    806:         && !cellHasTerrainFlag(player.xLoc, player.yLoc, (T_ENTANGLES | T_OBSTRUCTS_PASSABILITY))) {
                    807:         rogue.inWater = true;
                    808:     }
                    809:
                    810:     if (levels[rogue.depthLevel - 1].visited && rogue.patchVersion >= 3) {
                    811:         mapToStairs = allocGrid();
                    812:         mapToPit = allocGrid();
                    813:         fillGrid(mapToStairs, 0);
                    814:         fillGrid(mapToPit, 0);
                    815:         calculateDistances(mapToStairs, player.xLoc, player.yLoc, T_PATHING_BLOCKER, NULL, true, true);
                    816:         calculateDistances(mapToPit, levels[rogue.depthLevel-1].playerExitedVia[0],
                    817:                            levels[rogue.depthLevel-1].playerExitedVia[1], T_PATHING_BLOCKER, NULL, true, true);
                    818:         for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
                    819:             restoreMonster(monst, mapToStairs, mapToPit);
                    820:         }
                    821:         freeGrid(mapToStairs);
                    822:         freeGrid(mapToPit);
                    823:     }
                    824:
                    825:     updateMapToShore();
                    826:     updateVision(true);
                    827:     rogue.aggroRange = currentAggroValue();
                    828:
                    829:     // update monster states so none are hunting if there is no scent and they can't see the player
                    830:     for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
                    831:         updateMonsterState(monst);
                    832:     }
                    833:
                    834:     rogue.playbackBetweenTurns = true;
                    835:     displayLevel();
                    836:     refreshSideBar(-1, -1, false);
                    837:
                    838:     if (rogue.playerTurnNumber) {
                    839:         rogue.playerTurnNumber++; // Increment even though no time has passed.
                    840:     }
                    841:     RNGCheck();
                    842:     flushBufferToFile();
                    843:     deleteAllFlares(); // So discovering something on the same turn that you fall down a level doesn't flash stuff on the previous level.
                    844:     hideCursor();
                    845: }
                    846:
                    847: void freeGlobalDynamicGrid(short ***grid) {
                    848:     if (*grid) {
                    849:         freeGrid(*grid);
                    850:         *grid = NULL;
                    851:     }
                    852: }
                    853:
                    854: void freeCreature(creature *monst) {
                    855:     freeGlobalDynamicGrid(&(monst->mapToMe));
                    856:     freeGlobalDynamicGrid(&(monst->safetyMap));
                    857:     if (monst->carriedItem) {
                    858:         free(monst->carriedItem);
                    859:         monst->carriedItem = NULL;
                    860:     }
                    861:     if (monst->carriedMonster) {
                    862:         freeCreature(monst->carriedMonster);
                    863:         monst->carriedMonster = NULL;
                    864:     }
                    865:     free(monst);
                    866: }
                    867:
                    868: void emptyGraveyard() {
                    869:     creature *monst, *monst2;
                    870:     for (monst = graveyard->nextCreature; monst != NULL; monst = monst2) {
                    871:         monst2 = monst->nextCreature;
                    872:         freeCreature(monst);
                    873:     }
                    874:     graveyard->nextCreature = NULL;
                    875: }
                    876:
                    877: void freeEverything() {
                    878:     short i;
                    879:     creature *monst, *monst2;
                    880:     item *theItem, *theItem2;
                    881:
                    882: #ifdef AUDIT_RNG
                    883:     fclose(RNGLogFile);
                    884: #endif
                    885:
                    886:     freeGlobalDynamicGrid(&safetyMap);
                    887:     freeGlobalDynamicGrid(&allySafetyMap);
                    888:     freeGlobalDynamicGrid(&chokeMap);
                    889:     freeGlobalDynamicGrid(&rogue.mapToShore);
                    890:     freeGlobalDynamicGrid(&rogue.mapToSafeTerrain);
                    891:
                    892:     for (i=0; i<DEEPEST_LEVEL+1; i++) {
                    893:         for (monst = levels[i].monsters; monst != NULL; monst = monst2) {
                    894:             monst2 = monst->nextCreature;
                    895:             freeCreature(monst);
                    896:         }
                    897:         levels[i].monsters = NULL;
                    898:         for (monst = levels[i].dormantMonsters; monst != NULL; monst = monst2) {
                    899:             monst2 = monst->nextCreature;
                    900:             freeCreature(monst);
                    901:         }
                    902:         levels[i].dormantMonsters = NULL;
                    903:         for (theItem = levels[i].items; theItem != NULL; theItem = theItem2) {
                    904:             theItem2 = theItem->nextItem;
                    905:             deleteItem(theItem);
                    906:         }
                    907:         levels[i].items = NULL;
                    908:         if (levels[i].scentMap) {
                    909:             freeGrid(levels[i].scentMap);
                    910:             levels[i].scentMap = NULL;
                    911:         }
                    912:     }
                    913:     scentMap = NULL;
                    914:     for (monst = monsters; monst != NULL; monst = monst2) {
                    915:         monst2 = monst->nextCreature;
                    916:         freeCreature(monst);
                    917:     }
                    918:     monsters = NULL;
                    919:     for (monst = dormantMonsters; monst != NULL; monst = monst2) {
                    920:         monst2 = monst->nextCreature;
                    921:         freeCreature(monst);
                    922:     }
                    923:     dormantMonsters = NULL;
                    924:     for (monst = graveyard; monst != NULL; monst = monst2) {
                    925:         monst2 = monst->nextCreature;
                    926:         freeCreature(monst);
                    927:     }
                    928:     graveyard = NULL;
                    929:     for (monst = purgatory; monst != NULL; monst = monst2) {
                    930:         monst2 = monst->nextCreature;
                    931:         freeCreature(monst);
                    932:     }
                    933:     purgatory = NULL;
                    934:     for (theItem = floorItems; theItem != NULL; theItem = theItem2) {
                    935:         theItem2 = theItem->nextItem;
                    936:         deleteItem(theItem);
                    937:     }
                    938:     floorItems = NULL;
                    939:     for (theItem = packItems; theItem != NULL; theItem = theItem2) {
                    940:         theItem2 = theItem->nextItem;
                    941:         deleteItem(theItem);
                    942:     }
                    943:     packItems = NULL;
                    944:     for (theItem = monsterItemsHopper; theItem != NULL; theItem = theItem2) {
                    945:         theItem2 = theItem->nextItem;
                    946:         deleteItem(theItem);
                    947:     }
                    948:     monsterItemsHopper = NULL;
                    949:     for (i=0; i<MAX_WAYPOINT_COUNT; i++) {
                    950:         freeGrid(rogue.wpDistance[i]);
                    951:     }
                    952:
                    953:     deleteAllFlares();
                    954:     if (rogue.flares) {
                    955:         free(rogue.flares);
                    956:         rogue.flares = NULL;
                    957:     }
                    958:
                    959:     free(levels);
                    960:     levels = NULL;
                    961: }
                    962:
                    963: void gameOver(char *killedBy, boolean useCustomPhrasing) {
                    964:     short i, y;
                    965:     char buf[200], highScoreText[200], buf2[200];
                    966:     rogueHighScoresEntry theEntry;
                    967:     cellDisplayBuffer dbuf[COLS][ROWS];
                    968:     boolean playback;
                    969:     rogueEvent theEvent;
                    970:     item *theItem;
                    971:     char recordingFilename[BROGUE_FILENAME_MAX] = {0};
                    972:
                    973:     if (player.bookkeepingFlags & MB_IS_DYING) {
                    974:         // we've already been through this once; let's avoid overkill.
                    975:         return;
                    976:     }
                    977:
                    978:     player.bookkeepingFlags |= MB_IS_DYING;
                    979:     rogue.autoPlayingLevel = false;
                    980:     rogue.gameInProgress = false;
                    981:     flushBufferToFile();
                    982:
                    983:     if (rogue.quit) {
                    984:         if (rogue.playbackMode) {
                    985:             playback = rogue.playbackMode;
                    986:             rogue.playbackMode = false;
                    987:             message("(The player quit at this point.)", true);
                    988:             rogue.playbackMode = playback;
                    989:         }
                    990:     } else {
                    991:         playback = rogue.playbackMode;
                    992:         if (!D_IMMORTAL) {
                    993:             rogue.playbackMode = false;
                    994:         }
                    995:         strcpy(buf, "You die...");
                    996:         if (KEYBOARD_LABELS) {
                    997:             encodeMessageColor(buf, strlen(buf), &gray);
                    998:             strcat(buf, " (press 'i' to view your inventory)");
                    999:         }
                   1000:         player.currentHP = 0; // So it shows up empty in the side bar.
                   1001:         refreshSideBar(-1, -1, false);
                   1002:         messageWithColor(buf, &badMessageColor, false);
                   1003:         displayMoreSignWithoutWaitingForAcknowledgment();
                   1004:
                   1005:         do {
                   1006:             if (rogue.playbackMode) break;
                   1007:             nextBrogueEvent(&theEvent, false, false, false);
                   1008:             if (theEvent.eventType == KEYSTROKE
                   1009:                 && theEvent.param1 != ACKNOWLEDGE_KEY
                   1010:                 && theEvent.param1 != ESCAPE_KEY
                   1011:                 && theEvent.param1 != INVENTORY_KEY) {
                   1012:
                   1013:                 flashTemporaryAlert(" -- Press space or click to continue, or press 'i' to view inventory -- ", 1500);
                   1014:             } else if (theEvent.eventType == KEYSTROKE && theEvent.param1 == INVENTORY_KEY) {
                   1015:                 for (theItem = packItems->nextItem; theItem != NULL; theItem = theItem->nextItem) {
                   1016:                     identify(theItem);
                   1017:                     theItem->flags &= ~ITEM_MAGIC_DETECTED;
                   1018:                 }
                   1019:                 displayInventory(ALL_ITEMS, 0, 0, true, false);
                   1020:             }
                   1021:         } while (!(theEvent.eventType == KEYSTROKE && (theEvent.param1 == ACKNOWLEDGE_KEY || theEvent.param1 == ESCAPE_KEY)
                   1022:                    || theEvent.eventType == MOUSE_UP));
                   1023:
                   1024:         confirmMessages();
                   1025:
                   1026:         rogue.playbackMode = playback;
                   1027:     }
                   1028:
                   1029:     rogue.creaturesWillFlashThisTurn = false;
                   1030:
                   1031:     if (D_IMMORTAL && !rogue.quit) {
                   1032:         message("...but then you get better.", false);
                   1033:         player.currentHP = player.info.maxHP;
                   1034:         if (player.status[STATUS_NUTRITION] < 10) {
                   1035:             player.status[STATUS_NUTRITION] = STOMACH_SIZE;
                   1036:         }
                   1037:         player.bookkeepingFlags &= ~MB_IS_DYING;
                   1038:         rogue.gameInProgress = true;
                   1039:         return;
                   1040:     }
                   1041:
                   1042:     if (rogue.highScoreSaved) {
                   1043:         return;
                   1044:     }
                   1045:     rogue.highScoreSaved = true;
                   1046:
                   1047:     if (rogue.quit) {
                   1048:         blackOutScreen();
                   1049:     } else {
                   1050:         copyDisplayBuffer(dbuf, displayBuffer);
                   1051:         funkyFade(dbuf, &black, 0, 120, mapToWindowX(player.xLoc), mapToWindowY(player.yLoc), false);
                   1052:     }
                   1053:
                   1054:     if (useCustomPhrasing) {
                   1055:         sprintf(buf, "%s on depth %i", killedBy, rogue.depthLevel);
                   1056:     } else {
                   1057:         sprintf(buf, "Killed by a%s %s on depth %i", (isVowelish(killedBy) ? "n" : ""), killedBy,
                   1058:                 rogue.depthLevel);
                   1059:     }
                   1060:     theEntry.score = rogue.gold;
                   1061:     if (rogue.easyMode) {
                   1062:         theEntry.score /= 10;
                   1063:     }
                   1064:     strcpy(highScoreText, buf);
                   1065:     if (theEntry.score > 0) {
                   1066:         sprintf(buf2, " with %li gold", theEntry.score);
                   1067:         strcat(buf, buf2);
                   1068:     }
                   1069:     if (numberOfMatchingPackItems(AMULET, 0, 0, false) > 0) {
                   1070:         strcat(buf, ", amulet in hand");
                   1071:     }
                   1072:     strcat(buf, ".");
                   1073:     strcat(highScoreText, ".");
                   1074:
                   1075:     strcpy(theEntry.description, highScoreText);
                   1076:
                   1077:     if (!rogue.quit) {
                   1078:         printString(buf, (COLS - strLenWithoutEscapes(buf)) / 2, ROWS / 2, &gray, &black, 0);
                   1079:
                   1080:         y = ROWS / 2 + 3;
                   1081:         for (i = 0; i < FEAT_COUNT; i++) {
                   1082:             //printf("\nConduct %i (%s) is %s.", i, featTable[i].name, rogue.featRecord[i] ? "true" : "false");
                   1083:             if (rogue.featRecord[i]
                   1084:                 && !featTable[i].initialValue) {
                   1085:
                   1086:                 sprintf(buf, "%s: %s", featTable[i].name, featTable[i].description);
                   1087:                 printString(buf, (COLS - strLenWithoutEscapes(buf)) / 2, y, &advancementMessageColor, &black, 0);
                   1088:                 y++;
                   1089:             }
                   1090:         }
                   1091:
                   1092:         displayMoreSign();
                   1093:     }
                   1094:
                   1095:     if (serverMode) {
                   1096:         blackOutScreen();
                   1097:         saveRecordingNoPrompt(recordingFilename);
                   1098:     } else {
                   1099:         if (!rogue.playbackMode && saveHighScore(theEntry)) {
                   1100:             printHighScores(true);
                   1101:         }
                   1102:         blackOutScreen();
                   1103:         saveRecording(recordingFilename);
                   1104:     }
                   1105:
                   1106:     if (!rogue.playbackMode) {
                   1107:         if (!rogue.quit) {
                   1108:             notifyEvent(GAMEOVER_DEATH, theEntry.score, 0, theEntry.description, recordingFilename);
                   1109:         } else {
                   1110:             notifyEvent(GAMEOVER_QUIT, theEntry.score, 0, theEntry.description, recordingFilename);
                   1111:         }
                   1112:     } else {
                   1113:         notifyEvent(GAMEOVER_RECORDING, 0, 0, "recording ended", "none");
                   1114:     }
                   1115:
                   1116:     rogue.gameHasEnded = true;
                   1117: }
                   1118:
                   1119: void victory(boolean superVictory) {
                   1120:     char buf[COLS*3], victoryVerb[20];
                   1121:     item *theItem;
                   1122:     short i, j, gemCount = 0;
                   1123:     unsigned long totalValue = 0;
                   1124:     rogueHighScoresEntry theEntry;
                   1125:     boolean qualified, isPlayback;
                   1126:     cellDisplayBuffer dbuf[COLS][ROWS];
                   1127:     char recordingFilename[BROGUE_FILENAME_MAX] = {0};
                   1128:
                   1129:     rogue.gameInProgress = false;
                   1130:     flushBufferToFile();
                   1131:
                   1132:     //
                   1133:     // First screen - Congratulations...
                   1134:     //
                   1135:     deleteMessages();
                   1136:     if (superVictory) {
                   1137:         message(    "Light streams through the portal, and you are teleported out of the dungeon.", false);
                   1138:         copyDisplayBuffer(dbuf, displayBuffer);
                   1139:         funkyFade(dbuf, &superVictoryColor, 0, 240, mapToWindowX(player.xLoc), mapToWindowY(player.yLoc), false);
                   1140:         displayMoreSign();
                   1141:         printString("Congratulations; you have transcended the Dungeons of Doom!                 ", mapToWindowX(0), mapToWindowY(-1), &black, &white, 0);
                   1142:         displayMoreSign();
                   1143:         clearDisplayBuffer(dbuf);
                   1144:         deleteMessages();
                   1145:         strcpy(displayedMessage[0], "You retire in splendor, forever renowned for your remarkable triumph.     ");
                   1146:     } else {
                   1147:         message(    "You are bathed in sunlight as you throw open the heavy doors.", false);
                   1148:         copyDisplayBuffer(dbuf, displayBuffer);
                   1149:         funkyFade(dbuf, &white, 0, 240, mapToWindowX(player.xLoc), mapToWindowY(player.yLoc), false);
                   1150:         displayMoreSign();
                   1151:         printString("Congratulations; you have escaped from the Dungeons of Doom!     ", mapToWindowX(0), mapToWindowY(-1), &black, &white, 0);
                   1152:         displayMoreSign();
                   1153:         clearDisplayBuffer(dbuf);
                   1154:         deleteMessages();
                   1155:         strcpy(displayedMessage[0], "You sell your treasures and live out your days in fame and glory.");
                   1156:     }
                   1157:
                   1158:     //
                   1159:     // Second screen - Show inventory and item's value
                   1160:     //
                   1161:     printString(displayedMessage[0], mapToWindowX(0), mapToWindowY(-1), &white, &black, dbuf);
                   1162:
                   1163:     plotCharToBuffer(G_GOLD, mapToWindowX(2), mapToWindowY(1), &yellow, &black, dbuf);
                   1164:     printString("Gold", mapToWindowX(4), mapToWindowY(1), &white, &black, dbuf);
                   1165:     sprintf(buf, "%li", rogue.gold);
                   1166:     printString(buf, mapToWindowX(60), mapToWindowY(1), &itemMessageColor, &black, dbuf);
                   1167:     totalValue += rogue.gold;
                   1168:
                   1169:     for (i = 4, theItem = packItems->nextItem; theItem != NULL; theItem = theItem->nextItem) {
                   1170:         if (theItem->category & GEM) {
                   1171:             gemCount += theItem->quantity;
                   1172:         }
                   1173:         if (theItem->category == AMULET && superVictory) {
                   1174:             plotCharToBuffer(G_AMULET, mapToWindowX(2), min(ROWS-1, i + 1), &yellow, &black, dbuf);
                   1175:             printString("The Birthright of Yendor", mapToWindowX(4), min(ROWS-1, i + 1), &itemMessageColor, &black, dbuf);
                   1176:             sprintf(buf, "%li", max(0, itemValue(theItem) * 2));
                   1177:             printString(buf, mapToWindowX(60), min(ROWS-1, i + 1), &itemMessageColor, &black, dbuf);
                   1178:             totalValue += max(0, itemValue(theItem) * 2);
                   1179:             i++;
                   1180:         } else {
                   1181:             identify(theItem);
                   1182:             itemName(theItem, buf, true, true, &white);
                   1183:             upperCase(buf);
                   1184:
                   1185:             plotCharToBuffer(theItem->displayChar, mapToWindowX(2), min(ROWS-1, i + 1), &yellow, &black, dbuf);
                   1186:             printString(buf, mapToWindowX(4), min(ROWS-1, i + 1), &white, &black, dbuf);
                   1187:
                   1188:             if (itemValue(theItem) > 0) {
                   1189:                 sprintf(buf, "%li", max(0, itemValue(theItem)));
                   1190:                 printString(buf, mapToWindowX(60), min(ROWS-1, i + 1), &itemMessageColor, &black, dbuf);
                   1191:             }
                   1192:
                   1193:             totalValue += max(0, itemValue(theItem));
                   1194:             i++;
                   1195:         }
                   1196:     }
                   1197:     i++;
                   1198:     printString("TOTAL:", mapToWindowX(2), min(ROWS-1, i + 1), &lightBlue, &black, dbuf);
                   1199:     sprintf(buf, "%li", totalValue);
                   1200:     printString(buf, mapToWindowX(60), min(ROWS-1, i + 1), &lightBlue, &black, dbuf);
                   1201:
                   1202:     funkyFade(dbuf, &white, 0, 120, COLS/2, ROWS/2, true);
                   1203:     displayMoreSign();
                   1204:
                   1205:     //
                   1206:     // Third screen - List of achievements with recording save prompt
                   1207:     //
                   1208:     blackOutScreen();
                   1209:
                   1210:     i = 4;
                   1211:     printString("Achievements", mapToWindowX(2), i++, &lightBlue, &black, NULL);
                   1212:
                   1213:     i++;
                   1214:     for (j = 0; i < ROWS && j < FEAT_COUNT; j++) {
                   1215:         if (rogue.featRecord[j]) {
                   1216:             sprintf(buf, "%s: %s", featTable[j].name, featTable[j].description);
                   1217:             printString(buf, mapToWindowX(2), i, &advancementMessageColor, &black, NULL);
                   1218:             i++;
                   1219:         }
                   1220:     }
                   1221:
                   1222:     strcpy(victoryVerb, superVictory ? "Mastered" : "Escaped");
                   1223:     if (gemCount == 0) {
                   1224:         sprintf(theEntry.description, "%s the Dungeons of Doom!", victoryVerb);
                   1225:     } else if (gemCount == 1) {
                   1226:         sprintf(theEntry.description, "%s the Dungeons of Doom with a lumenstone!", victoryVerb);
                   1227:     } else {
                   1228:         sprintf(theEntry.description, "%s the Dungeons of Doom with %i lumenstones!", victoryVerb, gemCount);
                   1229:     }
                   1230:
                   1231:     theEntry.score = totalValue;
                   1232:
                   1233:     if (rogue.easyMode) {
                   1234:         theEntry.score /= 10;
                   1235:     }
                   1236:
                   1237:     if (!rogue.wizard && !rogue.playbackMode) {
                   1238:         qualified = saveHighScore(theEntry);
                   1239:     } else {
                   1240:         qualified = false;
                   1241:     }
                   1242:
                   1243:     isPlayback = rogue.playbackMode;
                   1244:     rogue.playbackMode = false;
                   1245:     rogue.playbackMode = isPlayback;
                   1246:
                   1247:     if (serverMode) {
                   1248:         // There's no save recording prompt, so let the player see achievements.
                   1249:         displayMoreSign();
                   1250:         saveRecordingNoPrompt(recordingFilename);
                   1251:     } else {
                   1252:         saveRecording(recordingFilename);
                   1253:         printHighScores(qualified);
                   1254:     }
                   1255:
                   1256:     if (!rogue.playbackMode) {
                   1257:         if (superVictory) {
                   1258:             notifyEvent(GAMEOVER_SUPERVICTORY, theEntry.score, 0, theEntry.description, recordingFilename);
                   1259:         } else {
                   1260:             notifyEvent(GAMEOVER_VICTORY, theEntry.score, 0, theEntry.description, recordingFilename);
                   1261:         }
                   1262:     } else {
                   1263:         notifyEvent(GAMEOVER_RECORDING, 0, 0, "recording ended", "none");
                   1264:     }
                   1265:
                   1266:     rogue.gameHasEnded = true;
                   1267: }
                   1268:
                   1269: void enableEasyMode() {
                   1270:     if (rogue.easyMode) {
                   1271:         message("Alas, all hope of salvation is lost. You shed scalding tears at your plight.", false);
                   1272:         return;
                   1273:     }
                   1274:     message("A dark presence surrounds you, whispering promises of stolen power.", true);
                   1275:     if (confirm("Succumb to demonic temptation (i.e. enable Easy Mode)?", false)) {
                   1276:         recordKeystroke(EASY_MODE_KEY, false, true);
                   1277:         message("An ancient and terrible evil burrows into your willing flesh!", true);
                   1278:         player.info.displayChar = '&';
                   1279:         rogue.easyMode = true;
                   1280:         refreshDungeonCell(player.xLoc, player.yLoc);
                   1281:         refreshSideBar(-1, -1, false);
                   1282:         message("Wracked by spasms, your body contorts into an ALL-POWERFUL AMPERSAND!!!", false);
                   1283:         message("You have a feeling that you will take 20% as much damage from now on.", false);
                   1284:         message("But great power comes at a great price -- specifically, a 90% income tax rate.", false);
                   1285:     } else {
                   1286:         message("The evil dissipates, hissing, from the air around you.", false);
                   1287:     }
                   1288: }
                   1289:
                   1290: // takes a flag of the form Fl(n) and returns n
                   1291: short unflag(unsigned long flag) {
                   1292:     short i;
                   1293:     for (i=0; i<32; i++) {
                   1294:         if (flag >> i == 1) {
                   1295:             return i;
                   1296:         }
                   1297:     }
                   1298:     return -1;
                   1299: }

CVSweb