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