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

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

1.1     ! rubenllo    1: /*
        !             2:  *  Buttons.c
        !             3:  *  Brogue
        !             4:  *
        !             5:  *  Created by Brian Walker on 1/14/12.
        !             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: #include <limits.h>
        !            28:
        !            29: #define MENU_FLAME_PRECISION_FACTOR     10
        !            30: #define MENU_FLAME_RISE_SPEED           50
        !            31: #define MENU_FLAME_SPREAD_SPEED         20
        !            32: #define MENU_FLAME_COLOR_DRIFT_SPEED    500
        !            33: #define MENU_FLAME_FADE_SPEED           20
        !            34: #define MENU_FLAME_UPDATE_DELAY         50
        !            35: #define MENU_FLAME_ROW_PADDING          2
        !            36: #define MENU_TITLE_OFFSET_X             (-4)
        !            37: #define MENU_TITLE_OFFSET_Y             (-1)
        !            38:
        !            39: #define MENU_FLAME_COLOR_SOURCE_COUNT   1136
        !            40:
        !            41: #define MENU_FLAME_DENOMINATOR          (100 + MENU_FLAME_RISE_SPEED + MENU_FLAME_SPREAD_SPEED)
        !            42:
        !            43:
        !            44: void drawMenuFlames(signed short flames[COLS][(ROWS + MENU_FLAME_ROW_PADDING)][3], unsigned char mask[COLS][ROWS]) {
        !            45:     short i, j, versionStringLength;
        !            46:     color tempColor = {0};
        !            47:     const color *maskColor = &black;
        !            48:     char dchar;
        !            49:
        !            50:     versionStringLength = strLenWithoutEscapes(BROGUE_VERSION_STRING);
        !            51:
        !            52:     for (j=0; j<ROWS; j++) {
        !            53:         for (i=0; i<COLS; i++) {
        !            54:             if (j == ROWS - 1 && i >= COLS - versionStringLength) {
        !            55:                 dchar = BROGUE_VERSION_STRING[i - (COLS - versionStringLength)];
        !            56:             } else {
        !            57:                 dchar = ' ';
        !            58:             }
        !            59:
        !            60:             if (mask[i][j] == 100) {
        !            61:                 plotCharWithColor(dchar, i, j, &veryDarkGray, maskColor);
        !            62:             } else {
        !            63:                 tempColor = black;
        !            64:                 tempColor.red   = flames[i][j][0] / MENU_FLAME_PRECISION_FACTOR;
        !            65:                 tempColor.green = flames[i][j][1] / MENU_FLAME_PRECISION_FACTOR;
        !            66:                 tempColor.blue  = flames[i][j][2] / MENU_FLAME_PRECISION_FACTOR;
        !            67:                 if (mask[i][j] > 0) {
        !            68:                     applyColorAverage(&tempColor, maskColor, mask[i][j]);
        !            69:                 }
        !            70:                 plotCharWithColor(dchar, i, j, &veryDarkGray, &tempColor);
        !            71:             }
        !            72:         }
        !            73:     }
        !            74: }
        !            75:
        !            76: void updateMenuFlames(const color *colors[COLS][(ROWS + MENU_FLAME_ROW_PADDING)],
        !            77:                       signed short colorSources[MENU_FLAME_COLOR_SOURCE_COUNT][4],
        !            78:                       signed short flames[COLS][(ROWS + MENU_FLAME_ROW_PADDING)][3]) {
        !            79:
        !            80:     short i, j, k, l, x, y;
        !            81:     signed short tempFlames[COLS][3];
        !            82:     short colorSourceNumber, rand;
        !            83:
        !            84:     colorSourceNumber = 0;
        !            85:     for (j=0; j<(ROWS + MENU_FLAME_ROW_PADDING); j++) {
        !            86:         // Make a temp copy of the current row.
        !            87:         for (i=0; i<COLS; i++) {
        !            88:             for (k=0; k<3; k++) {
        !            89:                 tempFlames[i][k] = flames[i][j][k];
        !            90:             }
        !            91:         }
        !            92:
        !            93:         for (i=0; i<COLS; i++) {
        !            94:             // Each cell is the weighted average of the three color values below and itself.
        !            95:             // Weight of itself: 100
        !            96:             // Weight of left and right neighbors: MENU_FLAME_SPREAD_SPEED / 2 each
        !            97:             // Weight of below cell: MENU_FLAME_RISE_SPEED
        !            98:             // Divisor: 100 + MENU_FLAME_SPREAD_SPEED + MENU_FLAME_RISE_SPEED
        !            99:
        !           100:             // Itself:
        !           101:             for (k=0; k<3; k++) {
        !           102:                 flames[i][j][k] = 100 * flames[i][j][k] / MENU_FLAME_DENOMINATOR;
        !           103:             }
        !           104:
        !           105:             // Left and right neighbors:
        !           106:             for (l = -1; l <= 1; l += 2) {
        !           107:                 x = i + l;
        !           108:                 if (x == -1) {
        !           109:                     x = COLS - 1;
        !           110:                 } else if (x == COLS) {
        !           111:                     x = 0;
        !           112:                 }
        !           113:                 for (k=0; k<3; k++) {
        !           114:                     flames[i][j][k] += MENU_FLAME_SPREAD_SPEED * tempFlames[x][k] / 2 / MENU_FLAME_DENOMINATOR;
        !           115:                 }
        !           116:             }
        !           117:
        !           118:             // Below:
        !           119:             y = j + 1;
        !           120:             if (y < (ROWS + MENU_FLAME_ROW_PADDING)) {
        !           121:                 for (k=0; k<3; k++) {
        !           122:                     flames[i][j][k] += MENU_FLAME_RISE_SPEED * flames[i][y][k] / MENU_FLAME_DENOMINATOR;
        !           123:                 }
        !           124:             }
        !           125:
        !           126:             // Fade a little:
        !           127:             for (k=0; k<3; k++) {
        !           128:                 flames[i][j][k] = (1000 - MENU_FLAME_FADE_SPEED) * flames[i][j][k] / 1000;
        !           129:             }
        !           130:
        !           131:             if (colors[i][j]) {
        !           132:                 // If it's a color source tile:
        !           133:
        !           134:                 // First, cause the color to drift a little.
        !           135:                 for (k=0; k<4; k++) {
        !           136:                     colorSources[colorSourceNumber][k] += rand_range(-MENU_FLAME_COLOR_DRIFT_SPEED, MENU_FLAME_COLOR_DRIFT_SPEED);
        !           137:                     colorSources[colorSourceNumber][k] = clamp(colorSources[colorSourceNumber][k], 0, 1000);
        !           138:                 }
        !           139:
        !           140:                 // Then, add the color to this tile's flames.
        !           141:                 rand = colors[i][j]->rand * colorSources[colorSourceNumber][0] / 1000;
        !           142:                 flames[i][j][0] += (colors[i][j]->red   + (colors[i][j]->redRand    * colorSources[colorSourceNumber][1] / 1000) + rand) * MENU_FLAME_PRECISION_FACTOR;
        !           143:                 flames[i][j][1] += (colors[i][j]->green + (colors[i][j]->greenRand  * colorSources[colorSourceNumber][2] / 1000) + rand) * MENU_FLAME_PRECISION_FACTOR;
        !           144:                 flames[i][j][2] += (colors[i][j]->blue  + (colors[i][j]->blueRand   * colorSources[colorSourceNumber][3] / 1000) + rand) * MENU_FLAME_PRECISION_FACTOR;
        !           145:
        !           146:                 colorSourceNumber++;
        !           147:             }
        !           148:         }
        !           149:     }
        !           150: }
        !           151:
        !           152: // Takes a grid of values, each of which is 0 or 100, and fills in some middle values in the interstices.
        !           153: void antiAlias(unsigned char mask[COLS][ROWS]) {
        !           154:     short i, j, x, y, dir, nbCount;
        !           155:     const short intensity[5] = {0, 0, 35, 50, 60};
        !           156:
        !           157:     for (i=0; i<COLS; i++) {
        !           158:         for (j=0; j<ROWS; j++) {
        !           159:             if (mask[i][j] < 100) {
        !           160:                 nbCount = 0;
        !           161:                 for (dir=0; dir<4; dir++) {
        !           162:                     x = i + nbDirs[dir][0];
        !           163:                     y = j + nbDirs[dir][1];
        !           164:                     if (coordinatesAreInWindow(x, y) && mask[x][y] == 100) {
        !           165:                         nbCount++;
        !           166:                     }
        !           167:                 }
        !           168:                 mask[i][j] = intensity[nbCount];
        !           169:             }
        !           170:         }
        !           171:     }
        !           172: }
        !           173:
        !           174: #define MENU_TITLE_WIDTH    74
        !           175: #define MENU_TITLE_HEIGHT   19
        !           176:
        !           177: void initializeMenuFlames(boolean includeTitle,
        !           178:                           const color *colors[COLS][(ROWS + MENU_FLAME_ROW_PADDING)],
        !           179:                           color colorStorage[COLS],
        !           180:                           signed short colorSources[MENU_FLAME_COLOR_SOURCE_COUNT][4],
        !           181:                           signed short flames[COLS][(ROWS + MENU_FLAME_ROW_PADDING)][3],
        !           182:                           unsigned char mask[COLS][ROWS]) {
        !           183:     short i, j, k, colorSourceCount;
        !           184:     const char title[MENU_TITLE_HEIGHT][MENU_TITLE_WIDTH+1] = {
        !           185:         "########   ########       ######         #######   ####     ###  #########",
        !           186:         " ##   ###   ##   ###    ##     ###     ##      ##   ##       #    ##     #",
        !           187:         " ##    ##   ##    ##   ##       ###   ##        #   ##       #    ##     #",
        !           188:         " ##    ##   ##    ##   #    #    ##   #         #   ##       #    ##      ",
        !           189:         " ##    ##   ##    ##  ##   ##     ## ##             ##       #    ##    # ",
        !           190:         " ##   ##    ##   ##   ##   ###    ## ##             ##       #    ##    # ",
        !           191:         " ######     ## ###    ##   ####   ## ##             ##       #    ####### ",
        !           192:         " ##    ##   ##  ##    ##   ####   ## ##             ##       #    ##    # ",
        !           193:         " ##     ##  ##   ##   ##    ###   ## ##      #####  ##       #    ##    # ",
        !           194:         " ##     ##  ##   ##   ###    ##   ## ###       ##   ##       #    ##      ",
        !           195:         " ##     ##  ##    ##   ##    #    #   ##       ##   ##       #    ##      ",
        !           196:         " ##     ##  ##    ##   ###       ##   ###      ##   ###      #    ##     #",
        !           197:         " ##    ##   ##     ##   ###     ##     ###    ###    ###    #     ##     #",
        !           198:         "########   ####    ###    ######         #####        ######     #########",
        !           199:         "                            ##                                            ",
        !           200:         "                        ##########                                        ",
        !           201:         "                            ##                                            ",
        !           202:         "                            ##                                            ",
        !           203:         "                           ####                                           ",
        !           204:     };
        !           205:
        !           206:     for (i=0; i<COLS; i++) {
        !           207:         for (j=0; j<ROWS; j++) {
        !           208:             mask[i][j] = 0;
        !           209:         }
        !           210:     }
        !           211:
        !           212:     for (i=0; i<COLS; i++) {
        !           213:         for (j=0; j<(ROWS + MENU_FLAME_ROW_PADDING); j++) {
        !           214:             colors[i][j] = NULL;
        !           215:             for (k=0; k<3; k++) {
        !           216:                 flames[i][j][k] = 0;
        !           217:             }
        !           218:         }
        !           219:     }
        !           220:
        !           221:     // Seed source color random components.
        !           222:     for (i=0; i<MENU_FLAME_COLOR_SOURCE_COUNT; i++) {
        !           223:         for (k=0; k<4; k++) {
        !           224:             colorSources[i][k] = rand_range(0, 1000);
        !           225:         }
        !           226:     }
        !           227:
        !           228:     // Put some flame source along the bottom row.
        !           229:     colorSourceCount = 0;
        !           230:     for (i=0; i<COLS; i++) {
        !           231:         colorStorage[colorSourceCount] = flameSourceColor;
        !           232:         applyColorAverage(&(colorStorage[colorSourceCount]), &flameSourceColorSecondary, 100 - (smoothHiliteGradient(i, COLS - 1) + 25));
        !           233:
        !           234:         colors[i][(ROWS + MENU_FLAME_ROW_PADDING)-1] = &(colorStorage[colorSourceCount]);
        !           235:         colorSourceCount++;
        !           236:     }
        !           237:
        !           238:     if (includeTitle) {
        !           239:         // Wreathe the title in flames, and mask it in black.
        !           240:         for (i=0; i<MENU_TITLE_WIDTH; i++) {
        !           241:             for (j=0; j<MENU_TITLE_HEIGHT; j++) {
        !           242:                 if (title[j][i] != ' ') {
        !           243:                     colors[(COLS - MENU_TITLE_WIDTH)/2 + i + MENU_TITLE_OFFSET_X][(ROWS - MENU_TITLE_HEIGHT)/2 + j + MENU_TITLE_OFFSET_Y] = &flameTitleColor;
        !           244:                     colorSourceCount++;
        !           245:                     mask[(COLS - MENU_TITLE_WIDTH)/2 + i + MENU_TITLE_OFFSET_X][(ROWS - MENU_TITLE_HEIGHT)/2 + j + MENU_TITLE_OFFSET_Y] = 100;
        !           246:                 }
        !           247:             }
        !           248:         }
        !           249:
        !           250:         // Anti-alias the mask.
        !           251:         antiAlias(mask);
        !           252:     }
        !           253:
        !           254:     brogueAssert(colorSourceCount <= MENU_FLAME_COLOR_SOURCE_COUNT);
        !           255:
        !           256:     // Simulate the background flames for a while
        !           257:     for (i=0; i<100; i++) {
        !           258:         updateMenuFlames(colors, colorSources, flames);
        !           259:     }
        !           260:
        !           261: }
        !           262:
        !           263: void titleMenu() {
        !           264:     signed short flames[COLS][(ROWS + MENU_FLAME_ROW_PADDING)][3]; // red, green and blue
        !           265:     signed short colorSources[MENU_FLAME_COLOR_SOURCE_COUNT][4]; // red, green, blue, and rand, one for each color source (no more than MENU_FLAME_COLOR_SOURCE_COUNT).
        !           266:     const color *colors[COLS][(ROWS + MENU_FLAME_ROW_PADDING)];
        !           267:     color colorStorage[COLS];
        !           268:     unsigned char mask[COLS][ROWS];
        !           269:     boolean controlKeyWasDown = false;
        !           270:
        !           271:     short i, b, x, y, button;
        !           272:     buttonState state;
        !           273:     brogueButton buttons[6];
        !           274:     char whiteColorEscape[10] = "";
        !           275:     char goldColorEscape[10] = "";
        !           276:     char newGameText[100] = "", customNewGameText[100] = "";
        !           277:     rogueEvent theEvent;
        !           278:     enum NGCommands buttonCommands[6] = {NG_NEW_GAME, NG_OPEN_GAME, NG_VIEW_RECORDING, NG_HIGH_SCORES, NG_QUIT};
        !           279:
        !           280:     cellDisplayBuffer shadowBuf[COLS][ROWS];
        !           281:
        !           282:     // Initialize the RNG so the flames aren't always the same.
        !           283:
        !           284:     seedRandomGenerator(0);
        !           285:
        !           286:     // Empty nextGamePath and nextGameSeed so that the buttons don't try to load an old game path or seed.
        !           287:     rogue.nextGamePath[0] = '\0';
        !           288:     rogue.nextGameSeed = 0;
        !           289:
        !           290:     // Initialize the title menu buttons.
        !           291:     encodeMessageColor(whiteColorEscape, 0, &white);
        !           292:     encodeMessageColor(goldColorEscape, 0, KEYBOARD_LABELS ? &itemMessageColor : &white);
        !           293:     sprintf(newGameText, "      %sN%sew Game      ", goldColorEscape, whiteColorEscape);
        !           294:     sprintf(customNewGameText, " %sN%sew Game (custom) ", goldColorEscape, whiteColorEscape);
        !           295:     b = 0;
        !           296:     button = -1;
        !           297:
        !           298:     initializeButton(&(buttons[b]));
        !           299:     strcpy(buttons[b].text, newGameText);
        !           300:     buttons[b].hotkey[0] = 'n';
        !           301:     buttons[b].hotkey[1] = 'N';
        !           302:     b++;
        !           303:
        !           304:     initializeButton(&(buttons[b]));
        !           305:     sprintf(buttons[b].text, "     %sO%spen Game      ", goldColorEscape, whiteColorEscape);
        !           306:     buttons[b].hotkey[0] = 'o';
        !           307:     buttons[b].hotkey[1] = 'O';
        !           308:     b++;
        !           309:
        !           310:     initializeButton(&(buttons[b]));
        !           311:     sprintf(buttons[b].text, "   %sV%siew Recording   ", goldColorEscape, whiteColorEscape);
        !           312:     buttons[b].hotkey[0] = 'v';
        !           313:     buttons[b].hotkey[1] = 'V';
        !           314:     b++;
        !           315:
        !           316:     initializeButton(&(buttons[b]));
        !           317:     sprintf(buttons[b].text, "    %sH%sigh Scores     ", goldColorEscape, whiteColorEscape);
        !           318:     buttons[b].hotkey[0] = 'h';
        !           319:     buttons[b].hotkey[1] = 'H';
        !           320:     b++;
        !           321:
        !           322:     initializeButton(&(buttons[b]));
        !           323:     sprintf(buttons[b].text, "        %sQ%suit        ", goldColorEscape, whiteColorEscape);
        !           324:     buttons[b].hotkey[0] = 'q';
        !           325:     buttons[b].hotkey[1] = 'Q';
        !           326:     b++;
        !           327:
        !           328:     x = COLS - 1 - 20 - 2;
        !           329:     y = ROWS - 1;
        !           330:     for (i = b-1; i >= 0; i--) {
        !           331:         y -= 2;
        !           332:         buttons[i].x = x;
        !           333:         buttons[i].y = y;
        !           334:         buttons[i].buttonColor = titleButtonColor;
        !           335:         buttons[i].flags |= B_WIDE_CLICK_AREA;
        !           336:     }
        !           337:
        !           338:     blackOutScreen();
        !           339:     clearDisplayBuffer(shadowBuf);
        !           340:     initializeButtonState(&state, buttons, b, x, y, 20, b*2-1);
        !           341:     rectangularShading(x, y, 20, b*2-1, &black, INTERFACE_OPACITY, shadowBuf);
        !           342:     drawButtonsInState(&state);
        !           343:
        !           344:     initializeMenuFlames(true, colors, colorStorage, colorSources, flames, mask);
        !           345:     rogue.creaturesWillFlashThisTurn = false; // total unconscionable hack
        !           346:
        !           347:     do {
        !           348:         if (isApplicationActive()) {
        !           349:             // Revert the display.
        !           350:             overlayDisplayBuffer(state.rbuf, NULL);
        !           351:
        !           352:             if (!controlKeyWasDown && controlKeyIsDown()) {
        !           353:                 strcpy(state.buttons[0].text, customNewGameText);
        !           354:                 drawButtonsInState(&state);
        !           355:                 buttonCommands[0] = NG_NEW_GAME_WITH_SEED;
        !           356:                 controlKeyWasDown = true;
        !           357:             } else if (controlKeyWasDown && !controlKeyIsDown()) {
        !           358:                 strcpy(state.buttons[0].text, newGameText);
        !           359:                 drawButtonsInState(&state);
        !           360:                 buttonCommands[0] = NG_NEW_GAME;
        !           361:                 controlKeyWasDown = false;
        !           362:             }
        !           363:
        !           364:             // Update the display.
        !           365:             updateMenuFlames(colors, colorSources, flames);
        !           366:             drawMenuFlames(flames, mask);
        !           367:             overlayDisplayBuffer(shadowBuf, NULL);
        !           368:             overlayDisplayBuffer(state.dbuf, NULL);
        !           369:
        !           370:             // Pause briefly.
        !           371:             if (pauseBrogue(MENU_FLAME_UPDATE_DELAY)) {
        !           372:                 // There was input during the pause! Get the input.
        !           373:                 nextBrogueEvent(&theEvent, true, false, true);
        !           374:
        !           375:                 // Process the input.
        !           376:                 button = processButtonInput(&state, NULL, &theEvent);
        !           377:             }
        !           378:
        !           379:         } else {
        !           380:             pauseBrogue(64);
        !           381:         }
        !           382:     } while (button == -1 && rogue.nextGame == NG_NOTHING);
        !           383:     drawMenuFlames(flames, mask);
        !           384:     if (button != -1) {
        !           385:         if (button == 0 && controlKeyIsDown()) {
        !           386:             // Should fix an issue with Linux/Windows ports that require moving the mouse after
        !           387:             // pressing control to get the button to change.
        !           388:             rogue.nextGame = NG_NEW_GAME_WITH_SEED;
        !           389:         } else {
        !           390:             rogue.nextGame = buttonCommands[button];
        !           391:         }
        !           392:     }
        !           393: }
        !           394:
        !           395: // Closes Brogue without any further prompts, animations, or user interaction.
        !           396: void quitImmediately() {
        !           397:     // If we are recording a game, save it.
        !           398:     if (rogue.recording) {
        !           399:         flushBufferToFile();
        !           400:         if (rogue.gameInProgress && !rogue.quit && !rogue.gameHasEnded) {
        !           401:             // Game isn't over yet, create a savegame.
        !           402:             saveGameNoPrompt();
        !           403:         } else {
        !           404:             // Save it as a recording.
        !           405:             char path[BROGUE_FILENAME_MAX];
        !           406:             saveRecordingNoPrompt(path);
        !           407:         }
        !           408:     }
        !           409:     exit(0);
        !           410: }
        !           411:
        !           412: void dialogAlert(char *message) {
        !           413:     cellDisplayBuffer rbuf[COLS][ROWS];
        !           414:
        !           415:     brogueButton OKButton;
        !           416:     initializeButton(&OKButton);
        !           417:     strcpy(OKButton.text, "     OK     ");
        !           418:     OKButton.hotkey[0] = RETURN_KEY;
        !           419:     OKButton.hotkey[1] = ACKNOWLEDGE_KEY;
        !           420:     printTextBox(message, COLS/3, ROWS/3, COLS/3, &white, &interfaceBoxColor, rbuf, &OKButton, 1);
        !           421:     overlayDisplayBuffer(rbuf, NULL);
        !           422: }
        !           423:
        !           424: boolean stringsExactlyMatch(const char *string1, const char *string2) {
        !           425:     short i;
        !           426:     for (i=0; string1[i] && string2[i]; i++) {
        !           427:         if (string1[i] != string2[i]) {
        !           428:             return false;
        !           429:         }
        !           430:     }
        !           431:     return string1[i] == string2[i];
        !           432: }
        !           433:
        !           434: // Used to compare the dates of two fileEntry variables
        !           435: // Returns (int):
        !           436: //      < 0 if 'b' date is lesser than 'a' date
        !           437: //      = 0 if 'b' date is equal to 'a' date,
        !           438: //      > 0 if 'b' date is greater than 'a' date
        !           439: int fileEntryCompareDates(const void *a, const void *b) {
        !           440:     fileEntry *f1 = (fileEntry *)a;
        !           441:     fileEntry *f2 = (fileEntry *)b;
        !           442:     time_t t1, t2;
        !           443:     double diff;
        !           444:
        !           445:     t1 = mktime(&f1->date);
        !           446:     t2 = mktime(&f2->date);
        !           447:     diff = difftime(t2, t1);
        !           448:
        !           449:     //char date_f1[11];
        !           450:     //char date_f2[11];
        !           451:     //strftime(date_f1, sizeof(date_f1), DATE_FORMAT, &f1->date);
        !           452:     //strftime(date_f2, sizeof(date_f2), DATE_FORMAT, &f2->date);
        !           453:     //printf("\nf1: %s\t%s",date_f1,f1->path);
        !           454:     //printf("\nf2: %s\t%s",date_f2,f2->path);
        !           455:     //printf("\ndiff: %f\n", diff);
        !           456:
        !           457:     return (int)diff;
        !           458: }
        !           459:
        !           460: #define FILES_ON_PAGE_MAX               (min(26, ROWS - 7)) // Two rows (top and bottom) for flames, two rows for border, one for prompt, one for heading.
        !           461: #define MAX_FILENAME_DISPLAY_LENGTH     53
        !           462: boolean dialogChooseFile(char *path, const char *suffix, const char *prompt) {
        !           463:     short i, j, count, x, y, width, height, suffixLength, pathLength, maxPathLength, currentPageStart;
        !           464:     brogueButton buttons[FILES_ON_PAGE_MAX + 2];
        !           465:     fileEntry *files;
        !           466:     boolean retval = false, again;
        !           467:     cellDisplayBuffer dbuf[COLS][ROWS], rbuf[COLS][ROWS];
        !           468:     color *dialogColor = &interfaceBoxColor;
        !           469:     char *membuf;
        !           470:     char fileDate [11];
        !           471:
        !           472:     suffixLength = strlen(suffix);
        !           473:     files = listFiles(&count, &membuf);
        !           474:     copyDisplayBuffer(rbuf, displayBuffer);
        !           475:     maxPathLength = strLenWithoutEscapes(prompt);
        !           476:
        !           477:     // First, we want to filter the list by stripping out any filenames that do not end with suffix.
        !           478:     // i is the entry we're testing, and j is the entry that we move it to if it qualifies.
        !           479:     for (i=0, j=0; i<count; i++) {
        !           480:         pathLength = strlen(files[i].path);
        !           481:         //printf("\nString 1: %s", &(files[i].path[(max(0, pathLength - suffixLength))]));
        !           482:         if (stringsExactlyMatch(&(files[i].path[(max(0, pathLength - suffixLength))]), suffix)) {
        !           483:
        !           484:             // This file counts!
        !           485:             if (i > j) {
        !           486:                 files[j] = files[i];
        !           487:                 //strftime(fileDate, sizeof(fileDate), DATE_FORMAT, &files[j].date);
        !           488:                 //printf("\nMatching file: %s\twith date: %s", files[j].path, fileDate);
        !           489:             }
        !           490:             j++;
        !           491:
        !           492:             // Keep track of the longest length.
        !           493:             if (min(pathLength, MAX_FILENAME_DISPLAY_LENGTH) + 10 > maxPathLength) {
        !           494:                 maxPathLength = min(pathLength, MAX_FILENAME_DISPLAY_LENGTH) + 10;
        !           495:             }
        !           496:         }
        !           497:     }
        !           498:     count = j;
        !           499:
        !           500:     // Once we have all relevant files, we sort them by date descending
        !           501:     qsort(files, count, sizeof(fileEntry), &fileEntryCompareDates);
        !           502:
        !           503:     currentPageStart = 0;
        !           504:
        !           505:     do { // Repeat to permit scrolling.
        !           506:         again = false;
        !           507:
        !           508:         for (i=0; i<min(count - currentPageStart, FILES_ON_PAGE_MAX); i++) {
        !           509:             initializeButton(&(buttons[i]));
        !           510:             buttons[i].flags &= ~(B_WIDE_CLICK_AREA | B_GRADIENT);
        !           511:             buttons[i].buttonColor = *dialogColor;
        !           512:             if (KEYBOARD_LABELS) {
        !           513:                 sprintf(buttons[i].text, "%c) ", 'a' + i);
        !           514:             } else {
        !           515:                 buttons[i].text[0] = '\0';
        !           516:             }
        !           517:             strncat(buttons[i].text, files[currentPageStart+i].path, MAX_FILENAME_DISPLAY_LENGTH);
        !           518:
        !           519:             // Clip off the file suffix from the button text.
        !           520:             buttons[i].text[strlen(buttons[i].text) - suffixLength] = '\0'; // Snip!
        !           521:             buttons[i].hotkey[0] = 'a' + i;
        !           522:             buttons[i].hotkey[1] = 'A' + i;
        !           523:
        !           524:             // Clip the filename length if necessary.
        !           525:             if (strlen(buttons[i].text) > MAX_FILENAME_DISPLAY_LENGTH) {
        !           526:                 strcpy(&(buttons[i].text[MAX_FILENAME_DISPLAY_LENGTH - 3]), "...");
        !           527:             }
        !           528:
        !           529:             //strftime(fileDate, sizeof(fileDate), DATE_FORMAT, &files[currentPageStart+i].date);
        !           530:             //printf("\nFound file: %s, with date: %s", files[currentPageStart+i].path, fileDate);
        !           531:         }
        !           532:
        !           533:         x = (COLS - maxPathLength) / 2;
        !           534:         width = maxPathLength;
        !           535:         height = min(count - currentPageStart, FILES_ON_PAGE_MAX) + 2;
        !           536:         y = max(4, (ROWS - height) / 2);
        !           537:
        !           538:         for (i=0; i<min(count - currentPageStart, FILES_ON_PAGE_MAX); i++) {
        !           539:             pathLength = strlen(buttons[i].text);
        !           540:             for (j=pathLength; j<(width - 10); j++) {
        !           541:                 buttons[i].text[j] = ' ';
        !           542:             }
        !           543:             buttons[i].text[j] = '\0';
        !           544:             strftime(fileDate, sizeof(fileDate), DATE_FORMAT, &files[currentPageStart+i].date);
        !           545:             strcpy(&(buttons[i].text[j]), fileDate);
        !           546:             buttons[i].x = x;
        !           547:             buttons[i].y = y + 1 + i;
        !           548:         }
        !           549:
        !           550:         if (count > FILES_ON_PAGE_MAX) {
        !           551:             // Create up and down arrows.
        !           552:             initializeButton(&(buttons[i]));
        !           553:             strcpy(buttons[i].text, "     *     ");
        !           554:             buttons[i].symbol[0] = G_UP_ARROW;
        !           555:             if (currentPageStart <= 0) {
        !           556:                 buttons[i].flags &= ~(B_ENABLED | B_DRAW);
        !           557:             } else {
        !           558:                 buttons[i].hotkey[0] = UP_ARROW;
        !           559:                 buttons[i].hotkey[1] = NUMPAD_8;
        !           560:                 buttons[i].hotkey[2] = PAGE_UP_KEY;
        !           561:             }
        !           562:             buttons[i].x = x + (width - 11)/2;
        !           563:             buttons[i].y = y;
        !           564:
        !           565:             i++;
        !           566:             initializeButton(&(buttons[i]));
        !           567:             strcpy(buttons[i].text, "     *     ");
        !           568:             buttons[i].symbol[0] = G_DOWN_ARROW;
        !           569:             if (currentPageStart + FILES_ON_PAGE_MAX >= count) {
        !           570:                 buttons[i].flags &= ~(B_ENABLED | B_DRAW);
        !           571:             } else {
        !           572:                 buttons[i].hotkey[0] = DOWN_ARROW;
        !           573:                 buttons[i].hotkey[1] = NUMPAD_2;
        !           574:                 buttons[i].hotkey[2] = PAGE_DOWN_KEY;
        !           575:             }
        !           576:             buttons[i].x = x + (width - 11)/2;
        !           577:             buttons[i].y = y + i;
        !           578:         }
        !           579:
        !           580:         if (count) {
        !           581:             clearDisplayBuffer(dbuf);
        !           582:             printString(prompt, x, y - 1, &itemMessageColor, dialogColor, dbuf);
        !           583:             rectangularShading(x - 1, y - 1, width + 1, height + 1, dialogColor, INTERFACE_OPACITY, dbuf);
        !           584:             overlayDisplayBuffer(dbuf, NULL);
        !           585:
        !           586: //          for (j=0; j<min(count - currentPageStart, FILES_ON_PAGE_MAX); j++) {
        !           587: //              strftime(fileDate, sizeof(fileDate), DATE_FORMAT, &files[currentPageStart+j].date);
        !           588: //              printf("\nSanity check BEFORE: %s, with date: %s", files[currentPageStart+j].path, fileDate);
        !           589: //              printf("\n   (button name)Sanity check BEFORE: %s", buttons[j].text);
        !           590: //          }
        !           591:
        !           592:             i = buttonInputLoop(buttons,
        !           593:                                 min(count - currentPageStart, FILES_ON_PAGE_MAX) + (count > FILES_ON_PAGE_MAX ? 2 : 0),
        !           594:                                 x,
        !           595:                                 y,
        !           596:                                 width,
        !           597:                                 height,
        !           598:                                 NULL);
        !           599:
        !           600: //          for (j=0; j<min(count - currentPageStart, FILES_ON_PAGE_MAX); j++) {
        !           601: //              strftime(fileDate, sizeof(fileDate), DATE_FORMAT, &files[currentPageStart+j].date);
        !           602: //              printf("\nSanity check AFTER: %s, with date: %s", files[currentPageStart+j].path, fileDate);
        !           603: //              printf("\n   (button name)Sanity check AFTER: %s", buttons[j].text);
        !           604: //          }
        !           605:
        !           606:             overlayDisplayBuffer(rbuf, NULL);
        !           607:
        !           608:             if (i < min(count - currentPageStart, FILES_ON_PAGE_MAX)) {
        !           609:                 if (i >= 0) {
        !           610:                     retval = true;
        !           611:                     strcpy(path, files[currentPageStart+i].path);
        !           612:                 } else { // i is -1
        !           613:                     retval = false;
        !           614:                 }
        !           615:             } else if (i == min(count - currentPageStart, FILES_ON_PAGE_MAX)) { // Up arrow
        !           616:                 again = true;
        !           617:                 currentPageStart -= FILES_ON_PAGE_MAX;
        !           618:             } else if (i == min(count - currentPageStart, FILES_ON_PAGE_MAX) + 1) { // Down arrow
        !           619:                 again = true;
        !           620:                 currentPageStart += FILES_ON_PAGE_MAX;
        !           621:             }
        !           622:         }
        !           623:
        !           624:     } while (again);
        !           625:
        !           626:     free(files);
        !           627:     free(membuf);
        !           628:
        !           629:     if (count == 0) {
        !           630:         dialogAlert("No applicable files found.");
        !           631:         return false;
        !           632:     } else {
        !           633:         return retval;
        !           634:     }
        !           635: }
        !           636:
        !           637: // This is the basic program loop.
        !           638: // When the program launches, or when a game ends, you end up here.
        !           639: // If the player has already said what he wants to do next
        !           640: // (by storing it in rogue.nextGame -- possibilities listed in enum NGCommands),
        !           641: // we'll do it. The path (rogue.nextGamePath) is essentially a parameter for this command, and
        !           642: // tells NG_VIEW_RECORDING and NG_OPEN_GAME which file to open. If there is a command but no
        !           643: // accompanying path, and it's a command that should take a path, then pop up a dialog to have
        !           644: // the player specify a path. If there is no command (i.e. if rogue.nextGame contains NG_NOTHING),
        !           645: // then we'll display the title screen so the player can choose.
        !           646: void mainBrogueJunction() {
        !           647:     rogueEvent theEvent;
        !           648:     char path[BROGUE_FILENAME_MAX], buf[100], seedDefault[100];
        !           649:     char *maxSeed = "4294967295"; // 2^32 - 1
        !           650:     short i, j, k;
        !           651:     boolean seedTooBig;
        !           652:
        !           653:     // clear screen and display buffer
        !           654:     for (i=0; i<COLS; i++) {
        !           655:         for (j=0; j<ROWS; j++) {
        !           656:             displayBuffer[i][j].character = 0;
        !           657:             displayBuffer[i][j].needsUpdate = false;
        !           658:             displayBuffer[i][j].opacity = 100;
        !           659:             for (k=0; k<3; k++) {
        !           660:                 displayBuffer[i][j].foreColorComponents[k] = 0;
        !           661:                 displayBuffer[i][j].backColorComponents[k] = 0;
        !           662:             }
        !           663:             plotCharWithColor(' ', i, j, &black, &black);
        !           664:         }
        !           665:     }
        !           666:
        !           667:     initializeLaunchArguments(&rogue.nextGame, rogue.nextGamePath, &rogue.nextGameSeed);
        !           668:
        !           669:     do {
        !           670:         rogue.gameHasEnded = false;
        !           671:         rogue.playbackFastForward = false;
        !           672:         rogue.playbackMode = false;
        !           673:         switch (rogue.nextGame) {
        !           674:             case NG_NOTHING:
        !           675:                 // Run the main menu to get a decision out of the player.
        !           676:                 titleMenu();
        !           677:                 break;
        !           678:             case NG_NEW_GAME:
        !           679:             case NG_NEW_GAME_WITH_SEED:
        !           680:                 rogue.nextGamePath[0] = '\0';
        !           681:                 randomNumbersGenerated = 0;
        !           682:
        !           683:                 rogue.playbackMode = false;
        !           684:                 rogue.playbackFastForward = false;
        !           685:                 rogue.playbackBetweenTurns = false;
        !           686:
        !           687:                 getAvailableFilePath(path, LAST_GAME_NAME, GAME_SUFFIX);
        !           688:                 strcat(path, GAME_SUFFIX);
        !           689:                 strcpy(currentFilePath, path);
        !           690:
        !           691:                 if (rogue.nextGame == NG_NEW_GAME_WITH_SEED) {
        !           692:                     if (rogue.nextGameSeed == 0) { // Prompt for seed; default is the previous game's seed.
        !           693:                         if (previousGameSeed == 0) {
        !           694:                             seedDefault[0] = '\0';
        !           695:                         } else {
        !           696:                             sprintf(seedDefault, "%lu", previousGameSeed);
        !           697:                         }
        !           698:                         if (getInputTextString(buf, "Generate dungeon with seed number:",
        !           699:                                                strlen(maxSeed),
        !           700:                                                seedDefault,
        !           701:                                                "",
        !           702:                                                TEXT_INPUT_NUMBERS,
        !           703:                                                true)
        !           704:                             && buf[0] != '\0') {
        !           705:                             seedTooBig = false;
        !           706:                             if (strlen(buf) == strlen(maxSeed)) {
        !           707:                                 for (i=0; maxSeed[i]; i++) {
        !           708:                                     if (maxSeed[i] > buf[i]) {
        !           709:                                         break; // we're good
        !           710:                                     } else if (maxSeed[i] < buf[i]) {
        !           711:                                         seedTooBig = true;
        !           712:                                         break;
        !           713:                                     }
        !           714:                                 }
        !           715:                             }
        !           716:                             sscanf(seedTooBig ? maxSeed : buf, "%lu", &rogue.nextGameSeed);
        !           717:                         } else {
        !           718:                             rogue.nextGame = NG_NOTHING;
        !           719:                             break; // Don't start a new game after all.
        !           720:                         }
        !           721:                     }
        !           722:                 } else {
        !           723:                     rogue.nextGameSeed = 0; // Seed based on clock.
        !           724:                 }
        !           725:
        !           726:                 rogue.nextGame = NG_NOTHING;
        !           727:                 initializeRogue(rogue.nextGameSeed);
        !           728:                 startLevel(rogue.depthLevel, 1); // descending into level 1
        !           729:
        !           730:                 mainInputLoop();
        !           731:                 if(serverMode) {
        !           732:                     rogue.nextGame = NG_QUIT;
        !           733:                 }
        !           734:                 freeEverything();
        !           735:                 break;
        !           736:             case NG_OPEN_GAME:
        !           737:                 rogue.nextGame = NG_NOTHING;
        !           738:                 path[0] = '\0';
        !           739:                 if (rogue.nextGamePath[0]) {
        !           740:                     strcpy(path, rogue.nextGamePath);
        !           741:                     rogue.nextGamePath[0] = '\0';
        !           742:                 } else {
        !           743:                     dialogChooseFile(path, GAME_SUFFIX, "Open saved game:");
        !           744:                     //chooseFile(path, "Open saved game: ", "Saved game", GAME_SUFFIX);
        !           745:                 }
        !           746:
        !           747:                 if (openFile(path)) {
        !           748:                     if (loadSavedGame()) {
        !           749:                         mainInputLoop();
        !           750:                     }
        !           751:                     freeEverything();
        !           752:                 } else {
        !           753:                     //dialogAlert("File not found.");
        !           754:                 }
        !           755:                 rogue.playbackMode = false;
        !           756:                 rogue.playbackOOS = false;
        !           757:
        !           758:                 if(serverMode) {
        !           759:                     rogue.nextGame = NG_QUIT;
        !           760:                 }
        !           761:                 break;
        !           762:             case NG_VIEW_RECORDING:
        !           763:                 rogue.nextGame = NG_NOTHING;
        !           764:
        !           765:                 path[0] = '\0';
        !           766:                 if (rogue.nextGamePath[0]) {
        !           767:                     strcpy(path, rogue.nextGamePath);
        !           768:                     rogue.nextGamePath[0] = '\0';
        !           769:                 } else {
        !           770:                     dialogChooseFile(path, RECORDING_SUFFIX, "View recording:");
        !           771:                     //chooseFile(path, "View recording: ", "Recording", RECORDING_SUFFIX);
        !           772:                 }
        !           773:
        !           774:                 if (openFile(path)) {
        !           775:                     randomNumbersGenerated = 0;
        !           776:                     rogue.playbackMode = true;
        !           777:                     initializeRogue(0); // Seed argument is ignored because we're in playback.
        !           778:                     if (!rogue.gameHasEnded) {
        !           779:                         startLevel(rogue.depthLevel, 1);
        !           780:                         rogue.playbackPaused = true;
        !           781:                         displayAnnotation(); // in case there's an annotation for turn 0
        !           782:                     }
        !           783:
        !           784:                     while(!rogue.gameHasEnded && rogue.playbackMode) {
        !           785:                         if (rogue.playbackPaused) {
        !           786:                             rogue.playbackPaused = false;
        !           787:                             pausePlayback();
        !           788:                         }
        !           789: #ifdef ENABLE_PLAYBACK_SWITCH
        !           790:                         // We are coming from the end of a recording the user has taken over.
        !           791:                         // No more event checks, that has already been handled
        !           792:                         if (rogue.gameHasEnded) {
        !           793:                             break;
        !           794:                         }
        !           795: #endif
        !           796:                         rogue.RNG = RNG_COSMETIC; // dancing terrain colors can't influence recordings
        !           797:                         rogue.playbackBetweenTurns = true;
        !           798:                         nextBrogueEvent(&theEvent, false, true, false);
        !           799:                         rogue.RNG = RNG_SUBSTANTIVE;
        !           800:
        !           801:                         executeEvent(&theEvent);
        !           802:                     }
        !           803:
        !           804:                     freeEverything();
        !           805:                 } else {
        !           806:                     // announce file not found
        !           807:                 }
        !           808:                 rogue.playbackMode = false;
        !           809:                 rogue.playbackOOS = false;
        !           810:
        !           811:                 if(serverMode) {
        !           812:                     rogue.nextGame = NG_QUIT;
        !           813:                 }
        !           814:                 break;
        !           815:             case NG_HIGH_SCORES:
        !           816:                 rogue.nextGame = NG_NOTHING;
        !           817:                 printHighScores(false);
        !           818:                 break;
        !           819:             case NG_QUIT:
        !           820:                 // No need to do anything.
        !           821:                 break;
        !           822:             default:
        !           823:                 break;
        !           824:         }
        !           825:     } while (rogue.nextGame != NG_QUIT);
        !           826: }

CVSweb