[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

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