Annotation of brogue-ce/src/brogue/IO.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: * IO.c
! 3: * Brogue
! 4: *
! 5: * Created by Brian Walker on 1/10/09.
! 6: * Copyright 2012. All rights reserved.
! 7: *
! 8: * This file is part of Brogue.
! 9: *
! 10: * This program is free software: you can redistribute it and/or modify
! 11: * it under the terms of the GNU Affero General Public License as
! 12: * published by the Free Software Foundation, either version 3 of the
! 13: * License, or (at your option) any later version.
! 14: *
! 15: * This program is distributed in the hope that it will be useful,
! 16: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 17: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 18: * GNU Affero General Public License for more details.
! 19: *
! 20: * You should have received a copy of the GNU Affero General Public License
! 21: * along with this program. If not, see <http://www.gnu.org/licenses/>.
! 22: */
! 23:
! 24: #include <math.h>
! 25: #include <time.h>
! 26:
! 27: #include "Rogue.h"
! 28: #include "IncludeGlobals.h"
! 29:
! 30: // Populates path[][] with a list of coordinates starting at origin and traversing down the map. Returns the number of steps in the path.
! 31: short getPlayerPathOnMap(short path[1000][2], short **map, short originX, short originY) {
! 32: short dir, x, y, steps;
! 33:
! 34: x = originX;
! 35: y = originY;
! 36:
! 37: dir = 0;
! 38:
! 39: for (steps = 0; dir != -1;) {
! 40: dir = nextStep(map, x, y, &player, false);
! 41: if (dir != -1) {
! 42: x += nbDirs[dir][0];
! 43: y += nbDirs[dir][1];
! 44: path[steps][0] = x;
! 45: path[steps][1] = y;
! 46: steps++;
! 47: brogueAssert(coordinatesAreInMap(x, y));
! 48: }
! 49: }
! 50: return steps;
! 51: }
! 52:
! 53: void reversePath(short path[1000][2], short steps) {
! 54: short i, x, y;
! 55:
! 56: for (i=0; i<steps / 2; i++) {
! 57: x = path[steps - i - 1][0];
! 58: y = path[steps - i - 1][1];
! 59:
! 60: path[steps - i - 1][0] = path[i][0];
! 61: path[steps - i - 1][1] = path[i][1];
! 62:
! 63: path[i][0] = x;
! 64: path[i][1] = y;
! 65: }
! 66: }
! 67:
! 68: void hilitePath(short path[1000][2], short steps, boolean unhilite) {
! 69: short i;
! 70: if (unhilite) {
! 71: for (i=0; i<steps; i++) {
! 72: brogueAssert(coordinatesAreInMap(path[i][0], path[i][1]));
! 73: pmap[path[i][0]][path[i][1]].flags &= ~IS_IN_PATH;
! 74: refreshDungeonCell(path[i][0], path[i][1]);
! 75: }
! 76: } else {
! 77: for (i=0; i<steps; i++) {
! 78: brogueAssert(coordinatesAreInMap(path[i][0], path[i][1]));
! 79: pmap[path[i][0]][path[i][1]].flags |= IS_IN_PATH;
! 80: refreshDungeonCell(path[i][0], path[i][1]);
! 81: }
! 82: }
! 83: }
! 84:
! 85: // More expensive than hilitePath(__, __, true), but you don't need access to the path itself.
! 86: void clearCursorPath() {
! 87: short i, j;
! 88:
! 89: if (!rogue.playbackMode) { // There are no cursor paths during playback.
! 90: for (i=1; i<DCOLS; i++) {
! 91: for (j=1; j<DROWS; j++) {
! 92: if (pmap[i][j].flags & IS_IN_PATH) {
! 93: pmap[i][j].flags &= ~IS_IN_PATH;
! 94: refreshDungeonCell(i, j);
! 95: }
! 96: }
! 97: }
! 98: }
! 99: }
! 100:
! 101: void hideCursor() {
! 102: // Drop out of cursor mode if we're in it, and hide the path either way.
! 103: rogue.cursorMode = false;
! 104: rogue.cursorPathIntensity = (rogue.cursorMode ? 50 : 20);
! 105: rogue.cursorLoc[0] = -1;
! 106: rogue.cursorLoc[1] = -1;
! 107: }
! 108:
! 109: void showCursor() {
! 110: // Return or enter turns on cursor mode. When the path is hidden, move the cursor to the player.
! 111: if (!coordinatesAreInMap(rogue.cursorLoc[0], rogue.cursorLoc[1])) {
! 112: rogue.cursorLoc[0] = player.xLoc;
! 113: rogue.cursorLoc[1] = player.yLoc;
! 114: rogue.cursorMode = true;
! 115: rogue.cursorPathIntensity = (rogue.cursorMode ? 50 : 20);
! 116: } else {
! 117: rogue.cursorMode = true;
! 118: rogue.cursorPathIntensity = (rogue.cursorMode ? 50 : 20);
! 119: }
! 120: }
! 121:
! 122: void getClosestValidLocationOnMap(short loc[2], short **map, short x, short y) {
! 123: short i, j, dist, closestDistance, lowestMapScore;
! 124:
! 125: closestDistance = 10000;
! 126: lowestMapScore = 10000;
! 127: for (i=1; i<DCOLS-1; i++) {
! 128: for (j=1; j<DROWS-1; j++) {
! 129: if (map[i][j] >= 0
! 130: && map[i][j] < 30000) {
! 131:
! 132: dist = (i - x)*(i - x) + (j - y)*(j - y);
! 133: //hiliteCell(i, j, &purple, min(dist / 2, 100), false);
! 134: if (dist < closestDistance
! 135: || dist == closestDistance && map[i][j] < lowestMapScore) {
! 136:
! 137: loc[0] = i;
! 138: loc[1] = j;
! 139: closestDistance = dist;
! 140: lowestMapScore = map[i][j];
! 141: }
! 142: }
! 143: }
! 144: }
! 145: }
! 146:
! 147: void processSnapMap(short **map) {
! 148: short **costMap;
! 149: enum directions dir;
! 150: short i, j, newX, newY;
! 151:
! 152: costMap = allocGrid();
! 153:
! 154: populateCreatureCostMap(costMap, &player);
! 155: fillGrid(map, 30000);
! 156: map[player.xLoc][player.yLoc] = 0;
! 157: dijkstraScan(map, costMap, true);
! 158: for (i = 0; i < DCOLS; i++) {
! 159: for (j = 0; j < DROWS; j++) {
! 160: if (cellHasTMFlag(i, j, TM_INVERT_WHEN_HIGHLIGHTED)) {
! 161: for (dir = 0; dir < 4; dir++) {
! 162: newX = i + nbDirs[dir][0];
! 163: newY = j + nbDirs[dir][1];
! 164: if (coordinatesAreInMap(newX, newY)
! 165: && map[newX][newY] >= 0
! 166: && map[newX][newY] < map[i][j]) {
! 167:
! 168: map[i][j] = map[newX][newY];
! 169: }
! 170: }
! 171: }
! 172: }
! 173: }
! 174:
! 175: freeGrid(costMap);
! 176: }
! 177:
! 178: // Displays a menu of buttons for various commands.
! 179: // Buttons will be disabled if not permitted based on the playback state.
! 180: // Returns the keystroke to effect the button's command, or -1 if canceled.
! 181: // Some buttons take effect in this function instead of returning a value,
! 182: // i.e. true colors mode and display stealth mode.
! 183: short actionMenu(short x, boolean playingBack) {
! 184: short buttonCount;
! 185: short y;
! 186: boolean takeActionOurselves[ROWS] = {false};
! 187: rogueEvent theEvent;
! 188:
! 189: brogueButton buttons[ROWS] = {{{0}}};
! 190: char yellowColorEscape[5] = "", whiteColorEscape[5] = "", darkGrayColorEscape[5] = "";
! 191: short i, j, longestName = 0, buttonChosen;
! 192: cellDisplayBuffer dbuf[COLS][ROWS], rbuf[COLS][ROWS];
! 193:
! 194: encodeMessageColor(yellowColorEscape, 0, &itemMessageColor);
! 195: encodeMessageColor(whiteColorEscape, 0, &white);
! 196: encodeMessageColor(darkGrayColorEscape, 0, &black);
! 197:
! 198: do {
! 199: for (i=0; i<ROWS; i++) {
! 200: initializeButton(&(buttons[i]));
! 201: buttons[i].buttonColor = interfaceBoxColor;
! 202: buttons[i].opacity = INTERFACE_OPACITY;
! 203: }
! 204:
! 205: buttonCount = 0;
! 206:
! 207: if (playingBack) {
! 208: #ifdef ENABLE_PLAYBACK_SWITCH
! 209: if (KEYBOARD_LABELS) {
! 210: sprintf(buttons[buttonCount].text, " %sP: %sPlay from here ", yellowColorEscape, whiteColorEscape);
! 211: } else {
! 212: strcpy(buttons[buttonCount].text, " Play from here ");
! 213: }
! 214: buttons[buttonCount].hotkey[0] = SWITCH_TO_PLAYING_KEY;
! 215: buttonCount++;
! 216:
! 217: sprintf(buttons[buttonCount].text, " %s---", darkGrayColorEscape);
! 218: buttons[buttonCount].flags &= ~B_ENABLED;
! 219: buttonCount++;
! 220: #endif
! 221: if (KEYBOARD_LABELS) {
! 222: sprintf(buttons[buttonCount].text, " %sk: %sFaster playback ", yellowColorEscape, whiteColorEscape);
! 223: } else {
! 224: strcpy(buttons[buttonCount].text, " Faster playback ");
! 225: }
! 226: buttons[buttonCount].hotkey[0] = UP_KEY;
! 227: buttons[buttonCount].hotkey[1] = UP_ARROW;
! 228: buttons[buttonCount].hotkey[2] = NUMPAD_8;
! 229: buttonCount++;
! 230: if (KEYBOARD_LABELS) {
! 231: sprintf(buttons[buttonCount].text, " %sj: %sSlower playback ", yellowColorEscape, whiteColorEscape);
! 232: } else {
! 233: strcpy(buttons[buttonCount].text, " Slower playback ");
! 234: }
! 235: buttons[buttonCount].hotkey[0] = DOWN_KEY;
! 236: buttons[buttonCount].hotkey[1] = DOWN_ARROW;
! 237: buttons[buttonCount].hotkey[2] = NUMPAD_2;
! 238: buttonCount++;
! 239: sprintf(buttons[buttonCount].text, " %s---", darkGrayColorEscape);
! 240: buttons[buttonCount].flags &= ~B_ENABLED;
! 241: buttonCount++;
! 242:
! 243: if (KEYBOARD_LABELS) {
! 244: sprintf(buttons[buttonCount].text, "%s0-9: %sFast forward to turn ", yellowColorEscape, whiteColorEscape);
! 245: } else {
! 246: strcpy(buttons[buttonCount].text, " Fast forward to turn ");
! 247: }
! 248: buttons[buttonCount].hotkey[0] = '0';
! 249: buttonCount++;
! 250: if (KEYBOARD_LABELS) {
! 251: sprintf(buttons[buttonCount].text, " %s<:%s Previous Level ", yellowColorEscape, whiteColorEscape);
! 252: } else {
! 253: strcpy(buttons[buttonCount].text, " Previous Level ");
! 254: }
! 255: buttons[buttonCount].hotkey[0] = ASCEND_KEY;
! 256: buttonCount++;
! 257: if (KEYBOARD_LABELS) {
! 258: sprintf(buttons[buttonCount].text, " %s>:%s Next Level ", yellowColorEscape, whiteColorEscape);
! 259: } else {
! 260: strcpy(buttons[buttonCount].text, " Next Level ");
! 261: }
! 262: buttons[buttonCount].hotkey[0] = DESCEND_KEY;
! 263: buttonCount++;
! 264: sprintf(buttons[buttonCount].text, " %s---", darkGrayColorEscape);
! 265: buttons[buttonCount].flags &= ~B_ENABLED;
! 266: buttonCount++;
! 267: } else {
! 268: if (KEYBOARD_LABELS) {
! 269: sprintf(buttons[buttonCount].text, " %sZ: %sRest until better ", yellowColorEscape, whiteColorEscape);
! 270: } else {
! 271: strcpy(buttons[buttonCount].text, " Rest until better ");
! 272: }
! 273: buttons[buttonCount].hotkey[0] = AUTO_REST_KEY;
! 274: buttonCount++;
! 275:
! 276: if (KEYBOARD_LABELS) {
! 277: sprintf(buttons[buttonCount].text, " %sA: %sAutopilot ", yellowColorEscape, whiteColorEscape);
! 278: } else {
! 279: strcpy(buttons[buttonCount].text, " Autopilot ");
! 280: }
! 281: buttons[buttonCount].hotkey[0] = AUTOPLAY_KEY;
! 282: buttonCount++;
! 283:
! 284: if (KEYBOARD_LABELS) {
! 285: sprintf(buttons[buttonCount].text, " %sT: %sRe-throw at last monster ", yellowColorEscape, whiteColorEscape);
! 286: } else {
! 287: strcpy(buttons[buttonCount].text, " Re-throw at last monster ");
! 288: }
! 289: buttons[buttonCount].hotkey[0] = RETHROW_KEY;
! 290: buttonCount++;
! 291:
! 292: if (!rogue.easyMode) {
! 293: if (KEYBOARD_LABELS) {
! 294: sprintf(buttons[buttonCount].text, " %s&: %sEasy mode ", yellowColorEscape, whiteColorEscape);
! 295: } else {
! 296: strcpy(buttons[buttonCount].text, " Easy mode ");
! 297: }
! 298: buttons[buttonCount].hotkey[0] = EASY_MODE_KEY;
! 299: buttonCount++;
! 300: }
! 301:
! 302: sprintf(buttons[buttonCount].text, " %s---", darkGrayColorEscape);
! 303: buttons[buttonCount].flags &= ~B_ENABLED;
! 304: buttonCount++;
! 305:
! 306: if(!serverMode) {
! 307: if (KEYBOARD_LABELS) {
! 308: sprintf(buttons[buttonCount].text, " %sS: %sSuspend game and quit ", yellowColorEscape, whiteColorEscape);
! 309: } else {
! 310: strcpy(buttons[buttonCount].text, " Suspend game and quit ");
! 311: }
! 312: buttons[buttonCount].hotkey[0] = SAVE_GAME_KEY;
! 313: buttonCount++;
! 314: if (KEYBOARD_LABELS) {
! 315: sprintf(buttons[buttonCount].text, " %sO: %sOpen suspended game ", yellowColorEscape, whiteColorEscape);
! 316: } else {
! 317: strcpy(buttons[buttonCount].text, " Open suspended game ");
! 318: }
! 319: buttons[buttonCount].hotkey[0] = LOAD_SAVED_GAME_KEY;
! 320: buttonCount++;
! 321: if (KEYBOARD_LABELS) {
! 322: sprintf(buttons[buttonCount].text, " %sV: %sView saved recording ", yellowColorEscape, whiteColorEscape);
! 323: } else {
! 324: strcpy(buttons[buttonCount].text, " View saved recording ");
! 325: }
! 326: buttons[buttonCount].hotkey[0] = VIEW_RECORDING_KEY;
! 327: buttonCount++;
! 328:
! 329: sprintf(buttons[buttonCount].text, " %s---", darkGrayColorEscape);
! 330: buttons[buttonCount].flags &= ~B_ENABLED;
! 331: buttonCount++;
! 332: }
! 333: }
! 334:
! 335: if (KEYBOARD_LABELS) {
! 336: sprintf(buttons[buttonCount].text, " %s\\: %s[%s] Hide color effects ", yellowColorEscape, whiteColorEscape, rogue.trueColorMode ? "X" : " ");
! 337: } else {
! 338: sprintf(buttons[buttonCount].text, " [%s] Hide color effects ", rogue.trueColorMode ? " " : "X");
! 339: }
! 340: buttons[buttonCount].hotkey[0] = TRUE_COLORS_KEY;
! 341: takeActionOurselves[buttonCount] = true;
! 342: buttonCount++;
! 343: if (KEYBOARD_LABELS) {
! 344: sprintf(buttons[buttonCount].text, " %s]: %s[%s] Display stealth range ", yellowColorEscape, whiteColorEscape, rogue.displayAggroRangeMode ? "X" : " ");
! 345: } else {
! 346: sprintf(buttons[buttonCount].text, " [%s] Show stealth range ", rogue.displayAggroRangeMode ? "X" : " ");
! 347: }
! 348: buttons[buttonCount].hotkey[0] = AGGRO_DISPLAY_KEY;
! 349: takeActionOurselves[buttonCount] = true;
! 350: buttonCount++;
! 351:
! 352: if (hasGraphics) {
! 353: if (KEYBOARD_LABELS) {
! 354: sprintf(buttons[buttonCount].text, " %sG: %s[%s] Enable graphics ", yellowColorEscape, whiteColorEscape, graphicsEnabled ? "X" : " ");
! 355: } else {
! 356: sprintf(buttons[buttonCount].text, " [%s] Enable graphics ", graphicsEnabled ? "X" : " ");
! 357: }
! 358: buttons[buttonCount].hotkey[0] = GRAPHICS_KEY;
! 359: takeActionOurselves[buttonCount] = true;
! 360: buttonCount++;
! 361: }
! 362:
! 363: sprintf(buttons[buttonCount].text, " %s---", darkGrayColorEscape);
! 364: buttons[buttonCount].flags &= ~B_ENABLED;
! 365: buttonCount++;
! 366:
! 367: if (KEYBOARD_LABELS) {
! 368: sprintf(buttons[buttonCount].text, " %sD: %sDiscovered items ", yellowColorEscape, whiteColorEscape);
! 369: } else {
! 370: strcpy(buttons[buttonCount].text, " Discovered items ");
! 371: }
! 372: buttons[buttonCount].hotkey[0] = DISCOVERIES_KEY;
! 373: buttonCount++;
! 374: if (KEYBOARD_LABELS) {
! 375: sprintf(buttons[buttonCount].text, " %s~: %sView dungeon seed ", yellowColorEscape, whiteColorEscape);
! 376: } else {
! 377: strcpy(buttons[buttonCount].text, " View dungeon seed ");
! 378: }
! 379: buttons[buttonCount].hotkey[0] = SEED_KEY;
! 380: buttonCount++;
! 381: if (KEYBOARD_LABELS) { // No help button if we're not in keyboard mode.
! 382: sprintf(buttons[buttonCount].text, " %s?: %sHelp ", yellowColorEscape, whiteColorEscape);
! 383: buttons[buttonCount].hotkey[0] = BROGUE_HELP_KEY;
! 384: buttonCount++;
! 385: }
! 386: sprintf(buttons[buttonCount].text, " %s---", darkGrayColorEscape);
! 387: buttons[buttonCount].flags &= ~B_ENABLED;
! 388: buttonCount++;
! 389:
! 390: if (KEYBOARD_LABELS) {
! 391: sprintf(buttons[buttonCount].text, " %sQ: %sQuit %s ", yellowColorEscape, whiteColorEscape, (playingBack ? "to title screen" : "without saving"));
! 392: } else {
! 393: sprintf(buttons[buttonCount].text, " Quit %s ", (playingBack ? "to title screen" : "without saving"));
! 394: }
! 395: buttons[buttonCount].hotkey[0] = QUIT_KEY;
! 396: buttonCount++;
! 397:
! 398: strcpy(buttons[buttonCount].text, " ");
! 399: buttons[buttonCount].flags &= ~B_ENABLED;
! 400: buttonCount++;
! 401:
! 402: for (i=0; i<buttonCount; i++) {
! 403: longestName = max(longestName, strLenWithoutEscapes(buttons[i].text));
! 404: }
! 405: if (x + longestName >= COLS) {
! 406: x = COLS - longestName - 1;
! 407: }
! 408: y = ROWS - buttonCount;
! 409: for (i=0; i<buttonCount; i++) {
! 410: buttons[i].x = x;
! 411: buttons[i].y = y + i;
! 412: for (j = strLenWithoutEscapes(buttons[i].text); j < longestName; j++) {
! 413: strcat(buttons[i].text, " "); // Schlemiel the Painter, but who cares.
! 414: }
! 415: }
! 416:
! 417: clearDisplayBuffer(dbuf);
! 418: rectangularShading(x - 1, y, longestName + 2, buttonCount, &black, INTERFACE_OPACITY / 2, dbuf);
! 419: overlayDisplayBuffer(dbuf, rbuf);
! 420: buttonChosen = buttonInputLoop(buttons, buttonCount, x - 1, y, longestName + 2, buttonCount, NULL);
! 421: overlayDisplayBuffer(rbuf, NULL);
! 422: if (buttonChosen == -1) {
! 423: return -1;
! 424: } else if (takeActionOurselves[buttonChosen]) {
! 425:
! 426: theEvent.eventType = KEYSTROKE;
! 427: theEvent.param1 = buttons[buttonChosen].hotkey[0];
! 428: theEvent.param2 = 0;
! 429: theEvent.shiftKey = theEvent.controlKey = false;
! 430: executeEvent(&theEvent);
! 431: } else {
! 432: return buttons[buttonChosen].hotkey[0];
! 433: }
! 434: } while (takeActionOurselves[buttonChosen]);
! 435: brogueAssert(false);
! 436: return -1;
! 437: }
! 438:
! 439: #define MAX_MENU_BUTTON_COUNT 5
! 440:
! 441: void initializeMenuButtons(buttonState *state, brogueButton buttons[5]) {
! 442: short i, x, buttonCount;
! 443: char goldTextEscape[MAX_MENU_BUTTON_COUNT] = "";
! 444: char whiteTextEscape[MAX_MENU_BUTTON_COUNT] = "";
! 445: color tempColor;
! 446:
! 447: encodeMessageColor(goldTextEscape, 0, KEYBOARD_LABELS ? &yellow : &white);
! 448: encodeMessageColor(whiteTextEscape, 0, &white);
! 449:
! 450: for (i=0; i<MAX_MENU_BUTTON_COUNT; i++) {
! 451: initializeButton(&(buttons[i]));
! 452: buttons[i].opacity = 75;
! 453: buttons[i].buttonColor = interfaceButtonColor;
! 454: buttons[i].y = ROWS - 1;
! 455: buttons[i].flags |= B_WIDE_CLICK_AREA;
! 456: buttons[i].flags &= ~B_KEYPRESS_HIGHLIGHT;
! 457: }
! 458:
! 459: buttonCount = 0;
! 460:
! 461: if (rogue.playbackMode) {
! 462: if (KEYBOARD_LABELS) {
! 463: sprintf(buttons[buttonCount].text, " Unpause (%sspace%s) ", goldTextEscape, whiteTextEscape);
! 464: } else {
! 465: strcpy(buttons[buttonCount].text, " Unpause ");
! 466: }
! 467: buttons[buttonCount].hotkey[0] = ACKNOWLEDGE_KEY;
! 468: buttonCount++;
! 469:
! 470: if (KEYBOARD_LABELS) {
! 471: sprintf(buttons[buttonCount].text, "Omniscience (%stab%s)", goldTextEscape, whiteTextEscape);
! 472: } else {
! 473: strcpy(buttons[buttonCount].text, " Omniscience ");
! 474: }
! 475: buttons[buttonCount].hotkey[0] = TAB_KEY;
! 476: buttonCount++;
! 477:
! 478: if (KEYBOARD_LABELS) {
! 479: sprintf(buttons[buttonCount].text, " Next Turn (%sl%s) ", goldTextEscape, whiteTextEscape);
! 480: } else {
! 481: strcpy(buttons[buttonCount].text, " Next Turn ");
! 482: }
! 483: buttons[buttonCount].hotkey[0] = RIGHT_KEY;
! 484: buttons[buttonCount].hotkey[1] = RIGHT_ARROW;
! 485: buttonCount++;
! 486:
! 487: strcpy(buttons[buttonCount].text, " Menu ");
! 488: buttonCount++;
! 489: } else {
! 490: sprintf(buttons[buttonCount].text, " E%sx%splore ", goldTextEscape, whiteTextEscape);
! 491: buttons[buttonCount].hotkey[0] = EXPLORE_KEY;
! 492: buttons[buttonCount].hotkey[1] = 'X';
! 493: buttonCount++;
! 494:
! 495: if (KEYBOARD_LABELS) {
! 496: sprintf(buttons[buttonCount].text, " Rest (%sz%s) ", goldTextEscape, whiteTextEscape);
! 497: } else {
! 498: strcpy(buttons[buttonCount].text, " Rest ");
! 499: }
! 500: buttons[buttonCount].hotkey[0] = REST_KEY;
! 501: buttonCount++;
! 502:
! 503: if (KEYBOARD_LABELS) {
! 504: sprintf(buttons[buttonCount].text, " Search (%ss%s) ", goldTextEscape, whiteTextEscape);
! 505: } else {
! 506: strcpy(buttons[buttonCount].text, " Search ");
! 507: }
! 508: buttons[buttonCount].hotkey[0] = SEARCH_KEY;
! 509: buttonCount++;
! 510:
! 511: strcpy(buttons[buttonCount].text, " Menu ");
! 512: buttonCount++;
! 513: }
! 514:
! 515: sprintf(buttons[4].text, " %sI%snventory ", goldTextEscape, whiteTextEscape);
! 516: buttons[4].hotkey[0] = INVENTORY_KEY;
! 517: buttons[4].hotkey[1] = 'I';
! 518:
! 519: x = mapToWindowX(0);
! 520: for (i=0; i<5; i++) {
! 521: buttons[i].x = x;
! 522: x += strLenWithoutEscapes(buttons[i].text) + 2; // Gap between buttons.
! 523: }
! 524:
! 525: initializeButtonState(state,
! 526: buttons,
! 527: 5,
! 528: mapToWindowX(0),
! 529: ROWS - 1,
! 530: COLS - mapToWindowX(0),
! 531: 1);
! 532:
! 533: for (i=0; i < 5; i++) {
! 534: drawButton(&(state->buttons[i]), BUTTON_NORMAL, state->rbuf);
! 535: }
! 536: for (i=0; i<COLS; i++) { // So the buttons stay (but are dimmed and desaturated) when inactive.
! 537: tempColor = colorFromComponents(state->rbuf[i][ROWS - 1].backColorComponents);
! 538: desaturate(&tempColor, 60);
! 539: applyColorAverage(&tempColor, &black, 50);
! 540: storeColorComponents(state->rbuf[i][ROWS - 1].backColorComponents, &tempColor);
! 541: tempColor = colorFromComponents(state->rbuf[i][ROWS - 1].foreColorComponents);
! 542: desaturate(&tempColor, 60);
! 543: applyColorAverage(&tempColor, &black, 50);
! 544: storeColorComponents(state->rbuf[i][ROWS - 1].foreColorComponents, &tempColor);
! 545: }
! 546: }
! 547:
! 548:
! 549: // This is basically the main loop for the game.
! 550: void mainInputLoop() {
! 551: short originLoc[2], pathDestination[2], oldTargetLoc[2] = { 0, 0 },
! 552: path[1000][2], steps, oldRNG, dir, newX, newY;
! 553: creature *monst;
! 554: item *theItem;
! 555: cellDisplayBuffer rbuf[COLS][ROWS];
! 556:
! 557: boolean canceled, targetConfirmed, tabKey, focusedOnMonster, focusedOnItem, focusedOnTerrain,
! 558: playingBack, doEvent, textDisplayed;
! 559:
! 560: rogueEvent theEvent;
! 561: short **costMap, **playerPathingMap, **cursorSnapMap;
! 562: brogueButton buttons[5] = {{{0}}};
! 563: buttonState state;
! 564: short buttonInput;
! 565: short backupCost;
! 566:
! 567: short *cursor = rogue.cursorLoc; // shorthand
! 568:
! 569: canceled = false;
! 570: rogue.cursorMode = false; // Controls whether the keyboard moves the cursor or the character.
! 571: steps = 0;
! 572:
! 573: rogue.cursorPathIntensity = (rogue.cursorMode ? 50 : 20);
! 574:
! 575: // Initialize buttons.
! 576: initializeMenuButtons(&state, buttons);
! 577:
! 578: playingBack = rogue.playbackMode;
! 579: rogue.playbackMode = false;
! 580: costMap = allocGrid();
! 581: playerPathingMap = allocGrid();
! 582: cursorSnapMap = allocGrid();
! 583:
! 584: cursor[0] = cursor[1] = -1;
! 585:
! 586: while (!rogue.gameHasEnded && (!playingBack || !canceled)) { // repeats until the game ends
! 587:
! 588: oldRNG = rogue.RNG;
! 589: rogue.RNG = RNG_COSMETIC;
! 590:
! 591: focusedOnMonster = focusedOnItem = focusedOnTerrain = false;
! 592: steps = 0;
! 593: clearCursorPath();
! 594:
! 595: originLoc[0] = player.xLoc;
! 596: originLoc[1] = player.yLoc;
! 597:
! 598: if (playingBack && rogue.cursorMode) {
! 599: temporaryMessage("Examine what? (<hjklyubn>, mouse, or <tab>)", false);
! 600: }
! 601:
! 602: if (!playingBack
! 603: && player.xLoc == cursor[0]
! 604: && player.yLoc == cursor[1]
! 605: && oldTargetLoc[0] == cursor[0]
! 606: && oldTargetLoc[1] == cursor[1]) {
! 607:
! 608: // Path hides when you reach your destination.
! 609: rogue.cursorMode = false;
! 610: rogue.cursorPathIntensity = (rogue.cursorMode ? 50 : 20);
! 611: cursor[0] = -1;
! 612: cursor[1] = -1;
! 613: }
! 614:
! 615: oldTargetLoc[0] = cursor[0];
! 616: oldTargetLoc[1] = cursor[1];
! 617:
! 618: populateCreatureCostMap(costMap, &player);
! 619:
! 620: fillGrid(playerPathingMap, 30000);
! 621: playerPathingMap[player.xLoc][player.yLoc] = 0;
! 622: dijkstraScan(playerPathingMap, costMap, true);
! 623: processSnapMap(cursorSnapMap);
! 624:
! 625: do {
! 626: textDisplayed = false;
! 627:
! 628: // Draw the cursor and path
! 629: if (coordinatesAreInMap(oldTargetLoc[0], oldTargetLoc[1])) {
! 630: refreshDungeonCell(oldTargetLoc[0], oldTargetLoc[1]); // Remove old cursor.
! 631: }
! 632: if (!playingBack) {
! 633: if (coordinatesAreInMap(oldTargetLoc[0], oldTargetLoc[1])) {
! 634: hilitePath(path, steps, true); // Unhilite old path.
! 635: }
! 636: if (coordinatesAreInMap(cursor[0], cursor[1])) {
! 637: if (cursorSnapMap[cursor[0]][cursor[1]] >= 0
! 638: && cursorSnapMap[cursor[0]][cursor[1]] < 30000) {
! 639:
! 640: pathDestination[0] = cursor[0];
! 641: pathDestination[1] = cursor[1];
! 642: } else {
! 643: // If the cursor is aimed at an inaccessible area, find the nearest accessible area to path toward.
! 644: getClosestValidLocationOnMap(pathDestination, cursorSnapMap, cursor[0], cursor[1]);
! 645: }
! 646:
! 647: fillGrid(playerPathingMap, 30000);
! 648: playerPathingMap[pathDestination[0]][pathDestination[1]] = 0;
! 649: backupCost = costMap[pathDestination[0]][pathDestination[1]];
! 650: costMap[pathDestination[0]][pathDestination[1]] = 1;
! 651: dijkstraScan(playerPathingMap, costMap, true);
! 652: costMap[pathDestination[0]][pathDestination[1]] = backupCost;
! 653: steps = getPlayerPathOnMap(path, playerPathingMap, player.xLoc, player.yLoc);
! 654:
! 655: // steps = getPlayerPathOnMap(path, playerPathingMap, pathDestination[0], pathDestination[1]) - 1; // Get new path.
! 656: // reversePath(path, steps); // Flip it around, back-to-front.
! 657:
! 658: if (steps >= 0) {
! 659: path[steps][0] = pathDestination[0];
! 660: path[steps][1] = pathDestination[1];
! 661: }
! 662: steps++;
! 663: // if (playerPathingMap[cursor[0]][cursor[1]] != 1
! 664: if (playerPathingMap[player.xLoc][player.yLoc] != 1
! 665: || pathDestination[0] != cursor[0]
! 666: || pathDestination[1] != cursor[1]) {
! 667:
! 668: hilitePath(path, steps, false); // Hilite new path.
! 669: }
! 670: }
! 671: }
! 672:
! 673: if (coordinatesAreInMap(cursor[0], cursor[1])) {
! 674: hiliteCell(cursor[0],
! 675: cursor[1],
! 676: &white,
! 677: (steps <= 0
! 678: || (path[steps-1][0] == cursor[0] && path[steps-1][1] == cursor[1])
! 679: || (!playingBack && distanceBetween(player.xLoc, player.yLoc, cursor[0], cursor[1]) <= 1) ? 100 : 25),
! 680: true);
! 681:
! 682: oldTargetLoc[0] = cursor[0];
! 683: oldTargetLoc[1] = cursor[1];
! 684:
! 685: monst = monsterAtLoc(cursor[0], cursor[1]);
! 686: theItem = itemAtLoc(cursor[0], cursor[1]);
! 687: if (monst != NULL && (canSeeMonster(monst) || rogue.playbackOmniscience)) {
! 688: rogue.playbackMode = playingBack;
! 689: refreshSideBar(cursor[0], cursor[1], false);
! 690: rogue.playbackMode = false;
! 691:
! 692: focusedOnMonster = true;
! 693: if (monst != &player && (!player.status[STATUS_HALLUCINATING] || rogue.playbackOmniscience)) {
! 694: printMonsterDetails(monst, rbuf);
! 695: textDisplayed = true;
! 696: }
! 697: } else if (theItem != NULL && playerCanSeeOrSense(cursor[0], cursor[1])) {
! 698: rogue.playbackMode = playingBack;
! 699: refreshSideBar(cursor[0], cursor[1], false);
! 700: rogue.playbackMode = false;
! 701:
! 702: focusedOnItem = true;
! 703: if (!player.status[STATUS_HALLUCINATING] || rogue.playbackOmniscience) {
! 704: printFloorItemDetails(theItem, rbuf);
! 705: textDisplayed = true;
! 706: }
! 707: } else if (cellHasTMFlag(cursor[0], cursor[1], TM_LIST_IN_SIDEBAR) && playerCanSeeOrSense(cursor[0], cursor[1])) {
! 708: rogue.playbackMode = playingBack;
! 709: refreshSideBar(cursor[0], cursor[1], false);
! 710: rogue.playbackMode = false;
! 711: focusedOnTerrain = true;
! 712: }
! 713:
! 714: printLocationDescription(cursor[0], cursor[1]);
! 715: }
! 716:
! 717: // Get the input!
! 718: rogue.playbackMode = playingBack;
! 719: doEvent = moveCursor(&targetConfirmed, &canceled, &tabKey, cursor, &theEvent, &state, !textDisplayed, rogue.cursorMode, true);
! 720: rogue.playbackMode = false;
! 721:
! 722: if (state.buttonChosen == 3) { // Actions menu button.
! 723: buttonInput = actionMenu(buttons[3].x - 4, playingBack); // Returns the corresponding keystroke.
! 724: if (buttonInput == -1) { // Canceled.
! 725: doEvent = false;
! 726: } else {
! 727: theEvent.eventType = KEYSTROKE;
! 728: theEvent.param1 = buttonInput;
! 729: theEvent.param2 = 0;
! 730: theEvent.shiftKey = theEvent.controlKey = false;
! 731: doEvent = true;
! 732: }
! 733: } else if (state.buttonChosen > -1) {
! 734: theEvent.eventType = KEYSTROKE;
! 735: theEvent.param1 = buttons[state.buttonChosen].hotkey[0];
! 736: theEvent.param2 = 0;
! 737: }
! 738: state.buttonChosen = -1;
! 739:
! 740: if (playingBack) {
! 741: if (canceled) {
! 742: rogue.cursorMode = false;
! 743: rogue.cursorPathIntensity = (rogue.cursorMode ? 50 : 20);
! 744: }
! 745:
! 746: if (theEvent.eventType == KEYSTROKE
! 747: && theEvent.param1 == ACKNOWLEDGE_KEY) { // To unpause by button during playback.
! 748: canceled = true;
! 749: } else {
! 750: canceled = false;
! 751: }
! 752: }
! 753:
! 754: if (focusedOnMonster || focusedOnItem || focusedOnTerrain) {
! 755: focusedOnMonster = false;
! 756: focusedOnItem = false;
! 757: focusedOnTerrain = false;
! 758: if (textDisplayed) {
! 759: overlayDisplayBuffer(rbuf, 0); // Erase the monster info window.
! 760: }
! 761: rogue.playbackMode = playingBack;
! 762: refreshSideBar(-1, -1, false);
! 763: rogue.playbackMode = false;
! 764: }
! 765:
! 766: if (tabKey && !playingBack) { // The tab key cycles the cursor through monsters, items and terrain features.
! 767: if (nextTargetAfter(&newX, &newY, cursor[0], cursor[1], true, true, true, true, false, theEvent.shiftKey)) {
! 768: cursor[0] = newX;
! 769: cursor[1] = newY;
! 770: }
! 771: }
! 772:
! 773: if (theEvent.eventType == KEYSTROKE
! 774: && (theEvent.param1 == ASCEND_KEY && cursor[0] == rogue.upLoc[0] && cursor[1] == rogue.upLoc[1]
! 775: || theEvent.param1 == DESCEND_KEY && cursor[0] == rogue.downLoc[0] && cursor[1] == rogue.downLoc[1])) {
! 776:
! 777: targetConfirmed = true;
! 778: doEvent = false;
! 779: }
! 780: } while (!targetConfirmed && !canceled && !doEvent && !rogue.gameHasEnded);
! 781:
! 782: if (coordinatesAreInMap(oldTargetLoc[0], oldTargetLoc[1])) {
! 783: refreshDungeonCell(oldTargetLoc[0], oldTargetLoc[1]); // Remove old cursor.
! 784: }
! 785:
! 786: restoreRNG;
! 787:
! 788: if (canceled && !playingBack) {
! 789: hideCursor();
! 790: confirmMessages();
! 791: } else if (targetConfirmed && !playingBack && coordinatesAreInMap(cursor[0], cursor[1])) {
! 792: if (theEvent.eventType == MOUSE_UP
! 793: && theEvent.controlKey
! 794: && steps > 1) {
! 795: // Control-clicking moves the player one step along the path.
! 796: for (dir=0;
! 797: dir < DIRECTION_COUNT && (player.xLoc + nbDirs[dir][0] != path[0][0] || player.yLoc + nbDirs[dir][1] != path[0][1]);
! 798: dir++);
! 799: playerMoves(dir);
! 800: } else if (D_WORMHOLING) {
! 801: travel(cursor[0], cursor[1], true);
! 802: } else {
! 803: confirmMessages();
! 804: if (originLoc[0] == cursor[0]
! 805: && originLoc[1] == cursor[1]) {
! 806:
! 807: confirmMessages();
! 808: } else if (abs(player.xLoc - cursor[0]) + abs(player.yLoc - cursor[1]) == 1 // horizontal or vertical
! 809: || (distanceBetween(player.xLoc, player.yLoc, cursor[0], cursor[1]) == 1 // includes diagonals
! 810: && (!diagonalBlocked(player.xLoc, player.yLoc, cursor[0], cursor[1], !rogue.playbackOmniscience)
! 811: || ((pmap[cursor[0]][cursor[1]].flags & HAS_MONSTER) && (monsterAtLoc(cursor[0], cursor[1])->info.flags & MONST_ATTACKABLE_THRU_WALLS)) // there's a turret there
! 812: || ((terrainFlags(cursor[0], cursor[1]) & T_OBSTRUCTS_PASSABILITY) && (terrainMechFlags(cursor[0], cursor[1]) & TM_PROMOTES_ON_PLAYER_ENTRY))))) { // there's a lever there
! 813: // Clicking one space away will cause the player to try to move there directly irrespective of path.
! 814: for (dir=0;
! 815: dir < DIRECTION_COUNT && (player.xLoc + nbDirs[dir][0] != cursor[0] || player.yLoc + nbDirs[dir][1] != cursor[1]);
! 816: dir++);
! 817: playerMoves(dir);
! 818: } else if (steps) {
! 819: travelRoute(path, steps);
! 820: }
! 821: }
! 822: } else if (doEvent) {
! 823: // If the player entered input during moveCursor() that wasn't a cursor movement command.
! 824: // Mainly, we want to filter out directional keystrokes when we're in cursor mode, since
! 825: // those should move the cursor but not the player.
! 826: brogueAssert(rogue.RNG == RNG_SUBSTANTIVE);
! 827: if (playingBack) {
! 828: rogue.playbackMode = true;
! 829: executePlaybackInput(&theEvent);
! 830: #ifdef ENABLE_PLAYBACK_SWITCH
! 831: if (!rogue.playbackMode) {
! 832: // Playback mode is off, user must have taken control
! 833: // Redraw buttons to reflect that
! 834: initializeMenuButtons(&state, buttons);
! 835: }
! 836: #endif
! 837: playingBack = rogue.playbackMode;
! 838: rogue.playbackMode = false;
! 839: } else {
! 840: executeEvent(&theEvent);
! 841: if (rogue.playbackMode) {
! 842: playingBack = true;
! 843: rogue.playbackMode = false;
! 844: confirmMessages();
! 845: break;
! 846: }
! 847: }
! 848: }
! 849: }
! 850:
! 851: rogue.playbackMode = playingBack;
! 852: refreshSideBar(-1, -1, false);
! 853: freeGrid(costMap);
! 854: freeGrid(playerPathingMap);
! 855: freeGrid(cursorSnapMap);
! 856: }
! 857:
! 858: // accuracy depends on how many clock cycles occur per second
! 859: #define MILLISECONDS (clock() * 1000 / CLOCKS_PER_SEC)
! 860:
! 861: #define MILLISECONDS_FOR_CAUTION 100
! 862:
! 863: void considerCautiousMode() {
! 864: /*
! 865: signed long oldMilliseconds = rogue.milliseconds;
! 866: rogue.milliseconds = MILLISECONDS;
! 867: clock_t i = clock();
! 868: printf("\n%li", i);
! 869: if (rogue.milliseconds - oldMilliseconds < MILLISECONDS_FOR_CAUTION) {
! 870: rogue.cautiousMode = true;
! 871: }*/
! 872: }
! 873:
! 874: // flags the entire window as needing to be redrawn at next flush.
! 875: // very low level -- does not interface with the guts of the game.
! 876: void refreshScreen() {
! 877: short i, j;
! 878:
! 879: for( i=0; i<COLS; i++ ) {
! 880: for( j=0; j<ROWS; j++ ) {
! 881: displayBuffer[i][j].needsUpdate = true;
! 882: }
! 883: }
! 884: commitDraws();
! 885: }
! 886:
! 887: // higher-level redraw
! 888: void displayLevel() {
! 889: short i, j;
! 890:
! 891: for( i=0; i<DCOLS; i++ ) {
! 892: for (j = DROWS-1; j >= 0; j--) {
! 893: refreshDungeonCell(i, j);
! 894: }
! 895: }
! 896: }
! 897:
! 898: // converts colors into components
! 899: void storeColorComponents(char components[3], const color *theColor) {
! 900: short rand = rand_range(0, theColor->rand);
! 901: components[0] = max(0, min(100, theColor->red + rand_range(0, theColor->redRand) + rand));
! 902: components[1] = max(0, min(100, theColor->green + rand_range(0, theColor->greenRand) + rand));
! 903: components[2] = max(0, min(100, theColor->blue + rand_range(0, theColor->blueRand) + rand));
! 904: }
! 905:
! 906: void bakeTerrainColors(color *foreColor, color *backColor, short x, short y) {
! 907: const short *vals;
! 908: const short neutralColors[8] = {1000, 1000, 1000, 1000, 0, 0, 0, 0};
! 909: if (rogue.trueColorMode) {
! 910: vals = neutralColors;
! 911: } else {
! 912: vals = &(terrainRandomValues[x][y][0]);
! 913: }
! 914:
! 915: const short foreRand = foreColor->rand * vals[6] / 1000;
! 916: const short backRand = backColor->rand * vals[7] / 1000;
! 917:
! 918: foreColor->red += foreColor->redRand * vals[0] / 1000 + foreRand;
! 919: foreColor->green += foreColor->greenRand * vals[1] / 1000 + foreRand;
! 920: foreColor->blue += foreColor->blueRand * vals[2] / 1000 + foreRand;
! 921: foreColor->redRand = foreColor->greenRand = foreColor->blueRand = foreColor->rand = 0;
! 922:
! 923: backColor->red += backColor->redRand * vals[3] / 1000 + backRand;
! 924: backColor->green += backColor->greenRand * vals[4] / 1000 + backRand;
! 925: backColor->blue += backColor->blueRand * vals[5] / 1000 + backRand;
! 926: backColor->redRand = backColor->greenRand = backColor->blueRand = backColor->rand = 0;
! 927:
! 928: if (foreColor->colorDances || backColor->colorDances) {
! 929: pmap[x][y].flags |= TERRAIN_COLORS_DANCING;
! 930: } else {
! 931: pmap[x][y].flags &= ~TERRAIN_COLORS_DANCING;
! 932: }
! 933: }
! 934:
! 935: void bakeColor(color *theColor) {
! 936: short rand;
! 937: rand = rand_range(0, theColor->rand);
! 938: theColor->red += rand_range(0, theColor->redRand) + rand;
! 939: theColor->green += rand_range(0, theColor->greenRand) + rand;
! 940: theColor->blue += rand_range(0, theColor->blueRand) + rand;
! 941: theColor->redRand = theColor->greenRand = theColor->blueRand = theColor->rand = 0;
! 942: }
! 943:
! 944: void shuffleTerrainColors(short percentOfCells, boolean refreshCells) {
! 945: enum directions dir;
! 946: short i, j;
! 947:
! 948: assureCosmeticRNG;
! 949:
! 950: for (i=0; i<DCOLS; i++) {
! 951: for(j=0; j<DROWS; j++) {
! 952: if (playerCanSeeOrSense(i, j)
! 953: && (!rogue.automationActive || !(rogue.playerTurnNumber % 5))
! 954: && ((pmap[i][j].flags & TERRAIN_COLORS_DANCING)
! 955: || (player.status[STATUS_HALLUCINATING] && playerCanDirectlySee(i, j)))
! 956: && (i != rogue.cursorLoc[0] || j != rogue.cursorLoc[1])
! 957: && (percentOfCells >= 100 || rand_range(1, 100) <= percentOfCells)) {
! 958:
! 959: for (dir=0; dir<DIRECTION_COUNT; dir++) {
! 960: terrainRandomValues[i][j][dir] += rand_range(-600, 600);
! 961: terrainRandomValues[i][j][dir] = clamp(terrainRandomValues[i][j][dir], 0, 1000);
! 962: }
! 963:
! 964: if (refreshCells) {
! 965: refreshDungeonCell(i, j);
! 966: }
! 967: }
! 968: }
! 969: }
! 970: restoreRNG;
! 971: }
! 972:
! 973: // if forecolor is too similar to back, darken or lighten it and return true.
! 974: // Assumes colors have already been baked (no random components).
! 975: boolean separateColors(color *fore, color *back) {
! 976: color f, b, *modifier;
! 977: short failsafe;
! 978: boolean madeChange;
! 979:
! 980: f = *fore;
! 981: b = *back;
! 982: f.red = clamp(f.red, 0, 100);
! 983: f.green = clamp(f.green, 0, 100);
! 984: f.blue = clamp(f.blue, 0, 100);
! 985: b.red = clamp(b.red, 0, 100);
! 986: b.green = clamp(b.green, 0, 100);
! 987: b.blue = clamp(b.blue, 0, 100);
! 988:
! 989: if (f.red + f.blue + f.green > 50 * 3) {
! 990: modifier = &black;
! 991: } else {
! 992: modifier = &white;
! 993: }
! 994:
! 995: madeChange = false;
! 996: failsafe = 10;
! 997:
! 998: while(COLOR_DIFF(f, b) < MIN_COLOR_DIFF && --failsafe) {
! 999: applyColorAverage(&f, modifier, 20);
! 1000: madeChange = true;
! 1001: }
! 1002:
! 1003: if (madeChange) {
! 1004: *fore = f;
! 1005: return true;
! 1006: } else {
! 1007: return false;
! 1008: }
! 1009: }
! 1010:
! 1011: void normColor(color *baseColor, const short aggregateMultiplier, const short colorTranslation) {
! 1012:
! 1013: baseColor->red += colorTranslation;
! 1014: baseColor->green += colorTranslation;
! 1015: baseColor->blue += colorTranslation;
! 1016: const short vectorLength = baseColor->red + baseColor->green + baseColor->blue;
! 1017:
! 1018: if (vectorLength != 0) {
! 1019: baseColor->red = baseColor->red * 300 / vectorLength * aggregateMultiplier / 100;
! 1020: baseColor->green = baseColor->green * 300 / vectorLength * aggregateMultiplier / 100;
! 1021: baseColor->blue = baseColor->blue * 300 / vectorLength * aggregateMultiplier / 100;
! 1022: }
! 1023: baseColor->redRand = 0;
! 1024: baseColor->greenRand = 0;
! 1025: baseColor->blueRand = 0;
! 1026: baseColor->rand = 0;
! 1027: }
! 1028:
! 1029: // Used to determine whether to draw a wall top glyph above
! 1030: static boolean glyphIsWallish(enum displayGlyph glyph) {
! 1031: switch (glyph) {
! 1032: case G_WALL:
! 1033: case G_OPEN_DOOR:
! 1034: case G_CLOSED_DOOR:
! 1035: case G_UP_STAIRS:
! 1036: case G_DOORWAY:
! 1037: case G_WALL_TOP:
! 1038: case G_LEVER:
! 1039: case G_LEVER_PULLED:
! 1040: case G_CLOSED_IRON_DOOR:
! 1041: case G_OPEN_IRON_DOOR:
! 1042: case G_TURRET:
! 1043: case G_GRANITE:
! 1044: case G_TORCH:
! 1045: case G_PORTCULLIS:
! 1046: return true;
! 1047:
! 1048: default:
! 1049: return false;
! 1050: }
! 1051: }
! 1052:
! 1053: static enum monsterTypes randomAnimateMonster() {
! 1054: /* Randomly pick an animate and vulnerable monster type. Used by
! 1055: getCellAppearance for hallucination effects. */
! 1056: static int listLength = 0;
! 1057: static enum monsterTypes animate[NUMBER_MONSTER_KINDS];
! 1058:
! 1059: if (listLength == 0) {
! 1060: for (int i=0; i < NUMBER_MONSTER_KINDS; i++) {
! 1061: if (!(monsterCatalog[i].flags & (MONST_INANIMATE | MONST_INVULNERABLE))) {
! 1062: animate[listLength++] = i;
! 1063: }
! 1064: }
! 1065: }
! 1066:
! 1067: return animate[rand_range(0, listLength - 1)];
! 1068: }
! 1069:
! 1070: // okay, this is kind of a beast...
! 1071: void getCellAppearance(short x, short y, enum displayGlyph *returnChar, color *returnForeColor, color *returnBackColor) {
! 1072: short bestBCPriority, bestFCPriority, bestCharPriority;
! 1073: short distance;
! 1074: enum displayGlyph cellChar = 0;
! 1075: color cellForeColor, cellBackColor, lightMultiplierColor = black, gasAugmentColor;
! 1076: boolean monsterWithDetectedItem = false, needDistinctness = false;
! 1077: short gasAugmentWeight = 0;
! 1078: creature *monst = NULL;
! 1079: item *theItem = NULL;
! 1080: enum tileType tile = NOTHING;
! 1081: const enum displayGlyph itemChars[] = {G_POTION, G_SCROLL, G_FOOD, G_WAND,
! 1082: G_STAFF, G_GOLD, G_ARMOR, G_WEAPON, G_RING, G_CHARM};
! 1083: enum dungeonLayers layer, maxLayer;
! 1084:
! 1085: assureCosmeticRNG;
! 1086:
! 1087: brogueAssert(coordinatesAreInMap(x, y));
! 1088:
! 1089: if (pmap[x][y].flags & HAS_MONSTER) {
! 1090: monst = monsterAtLoc(x, y);
! 1091: } else if (pmap[x][y].flags & HAS_DORMANT_MONSTER) {
! 1092: monst = dormantMonsterAtLoc(x, y);
! 1093: }
! 1094: if (monst) {
! 1095: monsterWithDetectedItem = (monst->carriedItem && (monst->carriedItem->flags & ITEM_MAGIC_DETECTED)
! 1096: && itemMagicPolarity(monst->carriedItem) && !canSeeMonster(monst));
! 1097: }
! 1098:
! 1099: if (monsterWithDetectedItem) {
! 1100: theItem = monst->carriedItem;
! 1101: } else {
! 1102: theItem = itemAtLoc(x, y);
! 1103: }
! 1104:
! 1105: if (!playerCanSeeOrSense(x, y)
! 1106: && !(pmap[x][y].flags & (ITEM_DETECTED | HAS_PLAYER))
! 1107: && (!monst || !monsterRevealed(monst))
! 1108: && !monsterWithDetectedItem
! 1109: && (pmap[x][y].flags & (DISCOVERED | MAGIC_MAPPED))
! 1110: && (pmap[x][y].flags & STABLE_MEMORY)) {
! 1111:
! 1112: // restore memory
! 1113: cellChar = pmap[x][y].rememberedAppearance.character;
! 1114: cellForeColor = colorFromComponents(pmap[x][y].rememberedAppearance.foreColorComponents);
! 1115: cellBackColor = colorFromComponents(pmap[x][y].rememberedAppearance.backColorComponents);
! 1116: } else {
! 1117: // Find the highest-priority fore color, back color and character.
! 1118: bestFCPriority = bestBCPriority = bestCharPriority = 10000;
! 1119:
! 1120: // Default to the appearance of floor.
! 1121: cellForeColor = *(tileCatalog[FLOOR].foreColor);
! 1122: cellBackColor = *(tileCatalog[FLOOR].backColor);
! 1123: cellChar = tileCatalog[FLOOR].displayChar;
! 1124:
! 1125: if (!(pmap[x][y].flags & DISCOVERED) && !rogue.playbackOmniscience) {
! 1126: if (pmap[x][y].flags & MAGIC_MAPPED) {
! 1127: maxLayer = LIQUID + 1; // Can see only dungeon and liquid layers with magic mapping.
! 1128: } else {
! 1129: maxLayer = 0; // Terrain shouldn't influence the tile appearance at all if it hasn't been discovered.
! 1130: }
! 1131: } else {
! 1132: maxLayer = NUMBER_TERRAIN_LAYERS;
! 1133: }
! 1134:
! 1135: for (layer = 0; layer < maxLayer; layer++) {
! 1136: // Gas shows up as a color average, not directly.
! 1137: if (pmap[x][y].layers[layer] && layer != GAS) {
! 1138: tile = pmap[x][y].layers[layer];
! 1139: if (rogue.playbackOmniscience && (tileCatalog[tile].mechFlags & TM_IS_SECRET)) {
! 1140: tile = dungeonFeatureCatalog[tileCatalog[tile].discoverType].tile;
! 1141: }
! 1142:
! 1143: if (tileCatalog[tile].drawPriority < bestFCPriority
! 1144: && tileCatalog[tile].foreColor) {
! 1145:
! 1146: cellForeColor = *(tileCatalog[tile].foreColor);
! 1147: bestFCPriority = tileCatalog[tile].drawPriority;
! 1148: }
! 1149: if (tileCatalog[tile].drawPriority < bestBCPriority
! 1150: && tileCatalog[tile].backColor) {
! 1151:
! 1152: cellBackColor = *(tileCatalog[tile].backColor);
! 1153: bestBCPriority = tileCatalog[tile].drawPriority;
! 1154: }
! 1155: if (tileCatalog[tile].drawPriority < bestCharPriority
! 1156: && tileCatalog[tile].displayChar) {
! 1157:
! 1158: cellChar = tileCatalog[tile].displayChar;
! 1159: bestCharPriority = tileCatalog[tile].drawPriority;
! 1160: needDistinctness = (tileCatalog[tile].mechFlags & TM_VISUALLY_DISTINCT) ? true : false;
! 1161: }
! 1162: }
! 1163: }
! 1164:
! 1165: if (rogue.trueColorMode) {
! 1166: lightMultiplierColor = colorMultiplier100;
! 1167: } else {
! 1168: colorMultiplierFromDungeonLight(x, y, &lightMultiplierColor);
! 1169: }
! 1170:
! 1171: if (pmap[x][y].layers[GAS]
! 1172: && tileCatalog[pmap[x][y].layers[GAS]].backColor) {
! 1173:
! 1174: gasAugmentColor = *(tileCatalog[pmap[x][y].layers[GAS]].backColor);
! 1175: if (rogue.trueColorMode) {
! 1176: gasAugmentWeight = 30;
! 1177: } else {
! 1178: gasAugmentWeight = min(90, 30 + pmap[x][y].volume);
! 1179: }
! 1180: }
! 1181:
! 1182: if (D_DISABLE_BACKGROUND_COLORS) {
! 1183: if (COLOR_DIFF(cellBackColor, black) > COLOR_DIFF(cellForeColor, black)) {
! 1184: cellForeColor = cellBackColor;
! 1185: }
! 1186: cellBackColor = black;
! 1187: needDistinctness = true;
! 1188: }
! 1189:
! 1190: if (pmap[x][y].flags & HAS_PLAYER) {
! 1191: cellChar = player.info.displayChar;
! 1192: cellForeColor = *(player.info.foreColor);
! 1193: needDistinctness = true;
! 1194: } else if (((pmap[x][y].flags & HAS_ITEM) && (pmap[x][y].flags & ITEM_DETECTED)
! 1195: && itemMagicPolarity(theItem)
! 1196: && !playerCanSeeOrSense(x, y))
! 1197: || monsterWithDetectedItem){
! 1198:
! 1199: int polarity = itemMagicPolarity(theItem);
! 1200: if (theItem->category == AMULET) {
! 1201: cellChar = G_AMULET;
! 1202: cellForeColor = white;
! 1203: } else if (polarity == -1) {
! 1204: cellChar = G_BAD_MAGIC;
! 1205: cellForeColor = badMessageColor;
! 1206: } else if (polarity == 1) {
! 1207: cellChar = G_GOOD_MAGIC;
! 1208: cellForeColor = goodMessageColor;
! 1209: } else {
! 1210: cellChar = 0;
! 1211: cellForeColor = white;
! 1212: }
! 1213:
! 1214: needDistinctness = true;
! 1215: } else if ((pmap[x][y].flags & HAS_MONSTER)
! 1216: && (playerCanSeeOrSense(x, y) || ((monst->info.flags & MONST_IMMOBILE) && (pmap[x][y].flags & DISCOVERED)))
! 1217: && (!monsterIsHidden(monst, &player) || rogue.playbackOmniscience)) {
! 1218: needDistinctness = true;
! 1219: if (player.status[STATUS_HALLUCINATING] > 0 && !(monst->info.flags & (MONST_INANIMATE | MONST_INVULNERABLE)) && !rogue.playbackOmniscience) {
! 1220: cellChar = monsterCatalog[randomAnimateMonster()].displayChar;
! 1221: cellForeColor = *(monsterCatalog[randomAnimateMonster()].foreColor);
! 1222: } else {
! 1223: cellChar = monst->info.displayChar;
! 1224: cellForeColor = *(monst->info.foreColor);
! 1225: if (monst->status[STATUS_INVISIBLE] || (monst->bookkeepingFlags & MB_SUBMERGED)) {
! 1226: // Invisible allies show up on the screen with a transparency effect.
! 1227: //cellForeColor = cellBackColor;
! 1228: applyColorAverage(&cellForeColor, &cellBackColor, 75);
! 1229: } else {
! 1230: if (monst->creatureState == MONSTER_ALLY && !(monst->info.flags & MONST_INANIMATE)) {
! 1231: if (rogue.trueColorMode) {
! 1232: cellForeColor = white;
! 1233: } else {
! 1234: applyColorAverage(&cellForeColor, &pink, 50);
! 1235: }
! 1236: }
! 1237: }
! 1238: //DEBUG if (monst->bookkeepingFlags & MB_LEADER) applyColorAverage(&cellBackColor, &purple, 50);
! 1239: }
! 1240: } else if (monst
! 1241: && monsterRevealed(monst)
! 1242: && !canSeeMonster(monst)) {
! 1243: if (player.status[STATUS_HALLUCINATING] && !rogue.playbackOmniscience) {
! 1244: cellChar = (rand_range(0, 1) ? 'X' : 'x');
! 1245: } else {
! 1246: cellChar = (monst->info.isLarge ? 'X' : 'x');
! 1247: }
! 1248: cellForeColor = white;
! 1249: lightMultiplierColor = white;
! 1250: if (!(pmap[x][y].flags & DISCOVERED)) {
! 1251: cellBackColor = black;
! 1252: gasAugmentColor = black;
! 1253: }
! 1254: } else if ((pmap[x][y].flags & HAS_ITEM) && !cellHasTerrainFlag(x, y, T_OBSTRUCTS_ITEMS)
! 1255: && (playerCanSeeOrSense(x, y) || ((pmap[x][y].flags & DISCOVERED) && !cellHasTerrainFlag(x, y, T_MOVES_ITEMS)))) {
! 1256: needDistinctness = true;
! 1257: if (player.status[STATUS_HALLUCINATING] && !rogue.playbackOmniscience) {
! 1258: cellChar = itemChars[rand_range(0, 9)];
! 1259: cellForeColor = itemColor;
! 1260: } else {
! 1261: theItem = itemAtLoc(x, y);
! 1262: cellChar = theItem->displayChar;
! 1263: cellForeColor = *(theItem->foreColor);
! 1264: // Remember the item was here
! 1265: pmap[x][y].rememberedItemCategory = theItem->category;
! 1266: pmap[x][y].rememberedItemKind = theItem->kind;
! 1267: pmap[x][y].rememberedItemQuantity = theItem->quantity;
! 1268: pmap[x][y].rememberedItemOriginDepth = theItem->originDepth;
! 1269: }
! 1270: } else if (playerCanSeeOrSense(x, y) || (pmap[x][y].flags & (DISCOVERED | MAGIC_MAPPED))) {
! 1271: // just don't want these to be plotted as black
! 1272: // Also, ensure we remember there are no items here
! 1273: pmap[x][y].rememberedItemCategory = 0;
! 1274: pmap[x][y].rememberedItemKind = 0;
! 1275: pmap[x][y].rememberedItemQuantity = 0;
! 1276: pmap[x][y].rememberedItemOriginDepth = 0;
! 1277: } else {
! 1278: *returnChar = ' ';
! 1279: *returnForeColor = black;
! 1280: *returnBackColor = undiscoveredColor;
! 1281:
! 1282: if (D_DISABLE_BACKGROUND_COLORS) *returnBackColor = black;
! 1283:
! 1284: restoreRNG;
! 1285: return;
! 1286: }
! 1287:
! 1288: if (gasAugmentWeight && ((pmap[x][y].flags & DISCOVERED) || rogue.playbackOmniscience)) {
! 1289: if (!rogue.trueColorMode || !needDistinctness) {
! 1290: applyColorAverage(&cellForeColor, &gasAugmentColor, gasAugmentWeight);
! 1291: }
! 1292: // phantoms create sillhouettes in gas clouds
! 1293: if ((pmap[x][y].flags & HAS_MONSTER)
! 1294: && monst->status[STATUS_INVISIBLE]
! 1295: && playerCanSeeOrSense(x, y)
! 1296: && !monsterRevealed(monst)
! 1297: && !monsterHiddenBySubmersion(monst, &player)) {
! 1298:
! 1299: if (player.status[STATUS_HALLUCINATING] && !rogue.playbackOmniscience) {
! 1300: cellChar = monsterCatalog[randomAnimateMonster()].displayChar;
! 1301: } else {
! 1302: cellChar = monst->info.displayChar;
! 1303: }
! 1304: cellForeColor = cellBackColor;
! 1305: }
! 1306: applyColorAverage(&cellBackColor, &gasAugmentColor, gasAugmentWeight);
! 1307: }
! 1308:
! 1309: if (!(pmap[x][y].flags & (ANY_KIND_OF_VISIBLE | ITEM_DETECTED | HAS_PLAYER))
! 1310: && !playerCanSeeOrSense(x, y)
! 1311: && (!monst || !monsterRevealed(monst)) && !monsterWithDetectedItem) {
! 1312:
! 1313: pmap[x][y].flags |= STABLE_MEMORY;
! 1314: pmap[x][y].rememberedAppearance.character = cellChar;
! 1315:
! 1316: if (rogue.trueColorMode) {
! 1317: bakeTerrainColors(&cellForeColor, &cellBackColor, x, y);
! 1318: }
! 1319:
! 1320: // store memory
! 1321: storeColorComponents(pmap[x][y].rememberedAppearance.foreColorComponents, &cellForeColor);
! 1322: storeColorComponents(pmap[x][y].rememberedAppearance.backColorComponents, &cellBackColor);
! 1323:
! 1324: applyColorAugment(&lightMultiplierColor, &basicLightColor, 100);
! 1325: if (!rogue.trueColorMode || !needDistinctness) {
! 1326: applyColorMultiplier(&cellForeColor, &lightMultiplierColor);
! 1327: }
! 1328: applyColorMultiplier(&cellBackColor, &lightMultiplierColor);
! 1329: bakeTerrainColors(&cellForeColor, &cellBackColor, x, y);
! 1330:
! 1331: // Then restore, so that it looks the same on this pass as it will when later refreshed.
! 1332: cellForeColor = colorFromComponents(pmap[x][y].rememberedAppearance.foreColorComponents);
! 1333: cellBackColor = colorFromComponents(pmap[x][y].rememberedAppearance.backColorComponents);
! 1334: }
! 1335: }
! 1336:
! 1337: // Smooth out walls: if there's a "wall-ish" tile drawn below us, just draw the wall top
! 1338: if ((cellChar == G_WALL || cellChar == G_GRANITE) && coordinatesAreInMap(x, y+1)
! 1339: && glyphIsWallish(displayBuffer[mapToWindowX(x)][mapToWindowY(y+1)].character)) {
! 1340: cellChar = G_WALL_TOP;
! 1341: }
! 1342:
! 1343: if (((pmap[x][y].flags & ITEM_DETECTED) || monsterWithDetectedItem
! 1344: || (monst && monsterRevealed(monst)))
! 1345: && !playerCanSeeOrSense(x, y)) {
! 1346: // do nothing
! 1347: } else if (!(pmap[x][y].flags & VISIBLE) && (pmap[x][y].flags & CLAIRVOYANT_VISIBLE)) {
! 1348: // can clairvoyantly see it
! 1349: if (rogue.trueColorMode) {
! 1350: lightMultiplierColor = basicLightColor;
! 1351: } else {
! 1352: applyColorAugment(&lightMultiplierColor, &basicLightColor, 100);
! 1353: }
! 1354: if (!rogue.trueColorMode || !needDistinctness) {
! 1355: applyColorMultiplier(&cellForeColor, &lightMultiplierColor);
! 1356: applyColorMultiplier(&cellForeColor, &clairvoyanceColor);
! 1357: }
! 1358: applyColorMultiplier(&cellBackColor, &lightMultiplierColor);
! 1359: applyColorMultiplier(&cellBackColor, &clairvoyanceColor);
! 1360: } else if (!(pmap[x][y].flags & VISIBLE) && (pmap[x][y].flags & TELEPATHIC_VISIBLE)) {
! 1361: // Can telepathically see it through another creature's eyes.
! 1362:
! 1363: applyColorAugment(&lightMultiplierColor, &basicLightColor, 100);
! 1364:
! 1365: if (!rogue.trueColorMode || !needDistinctness) {
! 1366: applyColorMultiplier(&cellForeColor, &lightMultiplierColor);
! 1367: applyColorMultiplier(&cellForeColor, &telepathyMultiplier);
! 1368: }
! 1369: applyColorMultiplier(&cellBackColor, &lightMultiplierColor);
! 1370: applyColorMultiplier(&cellBackColor, &telepathyMultiplier);
! 1371: } else if (!(pmap[x][y].flags & DISCOVERED) && (pmap[x][y].flags & MAGIC_MAPPED)) {
! 1372: // magic mapped only
! 1373: if (!rogue.playbackOmniscience) {
! 1374: needDistinctness = false;
! 1375: if (!rogue.trueColorMode || !needDistinctness) {
! 1376: applyColorMultiplier(&cellForeColor, &magicMapColor);
! 1377: }
! 1378: applyColorMultiplier(&cellBackColor, &magicMapColor);
! 1379: }
! 1380: } else if (!(pmap[x][y].flags & VISIBLE) && !rogue.playbackOmniscience) {
! 1381: // if it's not visible
! 1382:
! 1383: needDistinctness = false;
! 1384: if (rogue.inWater) {
! 1385: applyColorAverage(&cellForeColor, &black, 80);
! 1386: applyColorAverage(&cellBackColor, &black, 80);
! 1387: } else {
! 1388: if (!cellHasTMFlag(x, y, TM_BRIGHT_MEMORY)
! 1389: && (!rogue.trueColorMode || !needDistinctness)) {
! 1390:
! 1391: applyColorMultiplier(&cellForeColor, &memoryColor);
! 1392: applyColorAverage(&cellForeColor, &memoryOverlay, 25);
! 1393: }
! 1394: applyColorMultiplier(&cellBackColor, &memoryColor);
! 1395: applyColorAverage(&cellBackColor, &memoryOverlay, 25);
! 1396: }
! 1397: } else if (playerCanSeeOrSense(x, y) && rogue.playbackOmniscience && !(pmap[x][y].flags & ANY_KIND_OF_VISIBLE)) {
! 1398: // omniscience
! 1399: applyColorAugment(&lightMultiplierColor, &basicLightColor, 100);
! 1400: if (!rogue.trueColorMode || !needDistinctness) {
! 1401: applyColorMultiplier(&cellForeColor, &lightMultiplierColor);
! 1402: applyColorMultiplier(&cellForeColor, &omniscienceColor);
! 1403: }
! 1404: applyColorMultiplier(&cellBackColor, &lightMultiplierColor);
! 1405: applyColorMultiplier(&cellBackColor, &omniscienceColor);
! 1406: } else {
! 1407: if (!rogue.trueColorMode || !needDistinctness) {
! 1408: applyColorMultiplier(&cellForeColor, &lightMultiplierColor);
! 1409: }
! 1410: applyColorMultiplier(&cellBackColor, &lightMultiplierColor);
! 1411:
! 1412: if (player.status[STATUS_HALLUCINATING] && !rogue.trueColorMode) {
! 1413: randomizeColor(&cellForeColor, 40 * player.status[STATUS_HALLUCINATING] / 300 + 20);
! 1414: randomizeColor(&cellBackColor, 40 * player.status[STATUS_HALLUCINATING] / 300 + 20);
! 1415: }
! 1416: if (rogue.inWater) {
! 1417: applyColorMultiplier(&cellForeColor, &deepWaterLightColor);
! 1418: applyColorMultiplier(&cellBackColor, &deepWaterLightColor);
! 1419: }
! 1420: }
! 1421: // DEBUG cellBackColor.red = max(0,((scentMap[x][y] - rogue.scentTurnNumber) * 2) + 100);
! 1422: // DEBUG if (pmap[x][y].flags & KNOWN_TO_BE_TRAP_FREE) cellBackColor.red += 20;
! 1423: // DEBUG if (cellHasTerrainFlag(x, y, T_IS_FLAMMABLE)) cellBackColor.red += 50;
! 1424:
! 1425: if (pmap[x][y].flags & IS_IN_PATH) {
! 1426: if (cellHasTMFlag(x, y, TM_INVERT_WHEN_HIGHLIGHTED)) {
! 1427: swapColors(&cellForeColor, &cellBackColor);
! 1428: } else {
! 1429: if (!rogue.trueColorMode || !needDistinctness) {
! 1430: applyColorAverage(&cellForeColor, &yellow, rogue.cursorPathIntensity);
! 1431: }
! 1432: applyColorAverage(&cellBackColor, &yellow, rogue.cursorPathIntensity);
! 1433: }
! 1434: needDistinctness = true;
! 1435: }
! 1436:
! 1437: bakeTerrainColors(&cellForeColor, &cellBackColor, x, y);
! 1438:
! 1439: if (rogue.displayAggroRangeMode && (pmap[x][y].flags & IN_FIELD_OF_VIEW)) {
! 1440: distance = min(rogue.scentTurnNumber - scentMap[x][y], scentDistance(x, y, player.xLoc, player.yLoc));
! 1441: if (distance > rogue.aggroRange * 2) {
! 1442: applyColorAverage(&cellForeColor, &orange, 12);
! 1443: applyColorAverage(&cellBackColor, &orange, 12);
! 1444: applyColorAugment(&cellForeColor, &orange, 12);
! 1445: applyColorAugment(&cellBackColor, &orange, 12);
! 1446: }
! 1447: }
! 1448:
! 1449: if (rogue.trueColorMode
! 1450: && playerCanSeeOrSense(x, y)) {
! 1451:
! 1452: if (displayDetail[x][y] == DV_DARK) {
! 1453: applyColorMultiplier(&cellForeColor, &inDarknessMultiplierColor);
! 1454: applyColorMultiplier(&cellBackColor, &inDarknessMultiplierColor);
! 1455:
! 1456: applyColorAugment(&cellForeColor, &purple, 10);
! 1457: applyColorAugment(&cellBackColor, &white, -10);
! 1458: applyColorAverage(&cellBackColor, &purple, 20);
! 1459: } else if (displayDetail[x][y] == DV_LIT) {
! 1460:
! 1461: colorMultiplierFromDungeonLight(x, y, &lightMultiplierColor);
! 1462: normColor(&lightMultiplierColor, 175, 50);
! 1463: //applyColorMultiplier(&cellForeColor, &lightMultiplierColor);
! 1464: //applyColorMultiplier(&cellBackColor, &lightMultiplierColor);
! 1465: applyColorAugment(&cellForeColor, &lightMultiplierColor, 5);
! 1466: applyColorAugment(&cellBackColor, &lightMultiplierColor, 5);
! 1467: }
! 1468: }
! 1469:
! 1470: if (needDistinctness) {
! 1471: separateColors(&cellForeColor, &cellBackColor);
! 1472: }
! 1473:
! 1474: if (D_SCENT_VISION) {
! 1475: if (rogue.scentTurnNumber > (unsigned short) scentMap[x][y]) {
! 1476: cellBackColor.red = rogue.scentTurnNumber - (unsigned short) scentMap[x][y];
! 1477: cellBackColor.red = clamp(cellBackColor.red, 0, 100);
! 1478: } else {
! 1479: cellBackColor.green = abs(rogue.scentTurnNumber - (unsigned short) scentMap[x][y]);
! 1480: cellBackColor.green = clamp(cellBackColor.green, 0, 100);
! 1481: }
! 1482: }
! 1483:
! 1484: *returnChar = cellChar;
! 1485: *returnForeColor = cellForeColor;
! 1486: *returnBackColor = cellBackColor;
! 1487:
! 1488: if (D_DISABLE_BACKGROUND_COLORS) *returnBackColor = black;
! 1489: restoreRNG;
! 1490: }
! 1491:
! 1492: void refreshDungeonCell(short x, short y) {
! 1493: enum displayGlyph cellChar;
! 1494: color foreColor, backColor;
! 1495: brogueAssert(coordinatesAreInMap(x, y));
! 1496:
! 1497: getCellAppearance(x, y, &cellChar, &foreColor, &backColor);
! 1498: plotCharWithColor(cellChar, mapToWindowX(x), mapToWindowY(y), &foreColor, &backColor);
! 1499: }
! 1500:
! 1501: void applyColorMultiplier(color *baseColor, const color *multiplierColor) {
! 1502: baseColor->red = baseColor->red * multiplierColor->red / 100;
! 1503: baseColor->redRand = baseColor->redRand * multiplierColor->redRand / 100;
! 1504: baseColor->green = baseColor->green * multiplierColor->green / 100;
! 1505: baseColor->greenRand = baseColor->greenRand * multiplierColor->greenRand / 100;
! 1506: baseColor->blue = baseColor->blue * multiplierColor->blue / 100;
! 1507: baseColor->blueRand = baseColor->blueRand * multiplierColor->blueRand / 100;
! 1508: baseColor->rand = baseColor->rand * multiplierColor->rand / 100;
! 1509: //baseColor->colorDances *= multiplierColor->colorDances;
! 1510: return;
! 1511: }
! 1512:
! 1513: void applyColorAverage(color *baseColor, const color *newColor, short averageWeight) {
! 1514: short weightComplement = 100 - averageWeight;
! 1515: baseColor->red = (baseColor->red * weightComplement + newColor->red * averageWeight) / 100;
! 1516: baseColor->redRand = (baseColor->redRand * weightComplement + newColor->redRand * averageWeight) / 100;
! 1517: baseColor->green = (baseColor->green * weightComplement + newColor->green * averageWeight) / 100;
! 1518: baseColor->greenRand = (baseColor->greenRand * weightComplement + newColor->greenRand * averageWeight) / 100;
! 1519: baseColor->blue = (baseColor->blue * weightComplement + newColor->blue * averageWeight) / 100;
! 1520: baseColor->blueRand = (baseColor->blueRand * weightComplement + newColor->blueRand * averageWeight) / 100;
! 1521: baseColor->rand = (baseColor->rand * weightComplement + newColor->rand * averageWeight) / 100;
! 1522: baseColor->colorDances = (baseColor->colorDances || newColor->colorDances);
! 1523: return;
! 1524: }
! 1525:
! 1526: void applyColorAugment(color *baseColor, const color *augmentingColor, short augmentWeight) {
! 1527: baseColor->red += (augmentingColor->red * augmentWeight) / 100;
! 1528: baseColor->redRand += (augmentingColor->redRand * augmentWeight) / 100;
! 1529: baseColor->green += (augmentingColor->green * augmentWeight) / 100;
! 1530: baseColor->greenRand += (augmentingColor->greenRand * augmentWeight) / 100;
! 1531: baseColor->blue += (augmentingColor->blue * augmentWeight) / 100;
! 1532: baseColor->blueRand += (augmentingColor->blueRand * augmentWeight) / 100;
! 1533: baseColor->rand += (augmentingColor->rand * augmentWeight) / 100;
! 1534: return;
! 1535: }
! 1536:
! 1537: void applyColorScalar(color *baseColor, short scalar) {
! 1538: baseColor->red = baseColor->red * scalar / 100;
! 1539: baseColor->redRand = baseColor->redRand * scalar / 100;
! 1540: baseColor->green = baseColor->green * scalar / 100;
! 1541: baseColor->greenRand = baseColor->greenRand * scalar / 100;
! 1542: baseColor->blue = baseColor->blue * scalar / 100;
! 1543: baseColor->blueRand = baseColor->blueRand * scalar / 100;
! 1544: baseColor->rand = baseColor->rand * scalar / 100;
! 1545: }
! 1546:
! 1547: void applyColorBounds(color *baseColor, short lowerBound, short upperBound) {
! 1548: baseColor->red = clamp(baseColor->red, lowerBound, upperBound);
! 1549: baseColor->redRand = clamp(baseColor->redRand, lowerBound, upperBound);
! 1550: baseColor->green = clamp(baseColor->green, lowerBound, upperBound);
! 1551: baseColor->greenRand = clamp(baseColor->greenRand, lowerBound, upperBound);
! 1552: baseColor->blue = clamp(baseColor->blue, lowerBound, upperBound);
! 1553: baseColor->blueRand = clamp(baseColor->blueRand, lowerBound, upperBound);
! 1554: baseColor->rand = clamp(baseColor->rand, lowerBound, upperBound);
! 1555: }
! 1556:
! 1557: void desaturate(color *baseColor, short weight) {
! 1558: short avg;
! 1559: avg = (baseColor->red + baseColor->green + baseColor->blue) / 3 + 1;
! 1560: baseColor->red = baseColor->red * (100 - weight) / 100 + (avg * weight / 100);
! 1561: baseColor->green = baseColor->green * (100 - weight) / 100 + (avg * weight / 100);
! 1562: baseColor->blue = baseColor->blue * (100 - weight) / 100 + (avg * weight / 100);
! 1563:
! 1564: avg = (baseColor->redRand + baseColor->greenRand + baseColor->blueRand);
! 1565: baseColor->redRand = baseColor->redRand * (100 - weight) / 100;
! 1566: baseColor->greenRand = baseColor->greenRand * (100 - weight) / 100;
! 1567: baseColor->blueRand = baseColor->blueRand * (100 - weight) / 100;
! 1568:
! 1569: baseColor->rand += avg * weight / 3 / 100;
! 1570: }
! 1571:
! 1572: short randomizeByPercent(short input, short percent) {
! 1573: return (rand_range(input * (100 - percent) / 100, input * (100 + percent) / 100));
! 1574: }
! 1575:
! 1576: void randomizeColor(color *baseColor, short randomizePercent) {
! 1577: baseColor->red = randomizeByPercent(baseColor->red, randomizePercent);
! 1578: baseColor->green = randomizeByPercent(baseColor->green, randomizePercent);
! 1579: baseColor->blue = randomizeByPercent(baseColor->blue, randomizePercent);
! 1580: }
! 1581:
! 1582: void swapColors(color *color1, color *color2) {
! 1583: color tempColor = *color1;
! 1584: *color1 = *color2;
! 1585: *color2 = tempColor;
! 1586: }
! 1587:
! 1588: // Assumes colors are pre-baked.
! 1589: void blendAppearances(const color *fromForeColor, const color *fromBackColor, const enum displayGlyph fromChar,
! 1590: const color *toForeColor, const color *toBackColor, const enum displayGlyph toChar,
! 1591: color *retForeColor, color *retBackColor, enum displayGlyph *retChar,
! 1592: const short percent) {
! 1593: // Straight average of the back color:
! 1594: *retBackColor = *fromBackColor;
! 1595: applyColorAverage(retBackColor, toBackColor, percent);
! 1596:
! 1597: // Pick the character:
! 1598: if (percent >= 50) {
! 1599: *retChar = toChar;
! 1600: } else {
! 1601: *retChar = fromChar;
! 1602: }
! 1603:
! 1604: // Pick the method for blending the fore color.
! 1605: if (fromChar == toChar) {
! 1606: // If the character isn't changing, do a straight average.
! 1607: *retForeColor = *fromForeColor;
! 1608: applyColorAverage(retForeColor, toForeColor, percent);
! 1609: } else {
! 1610: // If it is changing, the first half blends to the current back color, and the second half blends to the final back color.
! 1611: if (percent >= 50) {
! 1612: *retForeColor = *retBackColor;
! 1613: applyColorAverage(retForeColor, toForeColor, (percent - 50) * 2);
! 1614: } else {
! 1615: *retForeColor = *fromForeColor;
! 1616: applyColorAverage(retForeColor, retBackColor, percent * 2);
! 1617: }
! 1618: }
! 1619: }
! 1620:
! 1621: void irisFadeBetweenBuffers(cellDisplayBuffer fromBuf[COLS][ROWS],
! 1622: cellDisplayBuffer toBuf[COLS][ROWS],
! 1623: short x, short y,
! 1624: short frameCount,
! 1625: boolean outsideIn) {
! 1626: short i, j, frame, percentBasis, thisCellPercent;
! 1627: boolean fastForward;
! 1628: color fromBackColor, toBackColor, fromForeColor, toForeColor, currentForeColor, currentBackColor;
! 1629: enum displayGlyph fromChar, toChar, currentChar;
! 1630: short completionMap[COLS][ROWS], maxDistance;
! 1631:
! 1632: fastForward = false;
! 1633: frame = 1;
! 1634:
! 1635: // Calculate the square of the maximum distance from (x, y) that the iris will have to spread.
! 1636: if (x < COLS / 2) {
! 1637: i = COLS - x;
! 1638: } else {
! 1639: i = x;
! 1640: }
! 1641: if (y < ROWS / 2) {
! 1642: j = ROWS - y;
! 1643: } else {
! 1644: j = y;
! 1645: }
! 1646: maxDistance = i*i + j*j;
! 1647:
! 1648: // Generate the initial completion map as a percent of maximum distance.
! 1649: for (i=0; i<COLS; i++) {
! 1650: for (j=0; j<ROWS; j++) {
! 1651: completionMap[i][j] = (i - x)*(i - x) + (j - y)*(j - y); // square of distance
! 1652: completionMap[i][j] = 100 * completionMap[i][j] / maxDistance; // percent of max distance
! 1653: if (outsideIn) {
! 1654: completionMap[i][j] -= 100; // translate to [-100, 0], with the origin at -100 and the farthest point at 0.
! 1655: } else {
! 1656: completionMap[i][j] *= -1; // translate to [-100, 0], with the origin at 0 and the farthest point at -100.
! 1657: }
! 1658: }
! 1659: }
! 1660:
! 1661: do {
! 1662: percentBasis = 10000 * frame / frameCount;
! 1663:
! 1664: for (i=0; i<COLS; i++) {
! 1665: for (j=0; j<ROWS; j++) {
! 1666: thisCellPercent = percentBasis * 3 / 100 + completionMap[i][j];
! 1667:
! 1668: fromBackColor = colorFromComponents(fromBuf[i][j].backColorComponents);
! 1669: fromForeColor = colorFromComponents(fromBuf[i][j].foreColorComponents);
! 1670: fromChar = fromBuf[i][j].character;
! 1671:
! 1672: toBackColor = colorFromComponents(toBuf[i][j].backColorComponents);
! 1673: toForeColor = colorFromComponents(toBuf[i][j].foreColorComponents);
! 1674: toChar = toBuf[i][j].character;
! 1675:
! 1676: blendAppearances(&fromForeColor, &fromBackColor, fromChar, &toForeColor, &toBackColor, toChar, ¤tForeColor, ¤tBackColor, ¤tChar, clamp(thisCellPercent, 0, 100));
! 1677: plotCharWithColor(currentChar, i, j, ¤tForeColor, ¤tBackColor);
! 1678: }
! 1679: }
! 1680:
! 1681: fastForward = pauseBrogue(16);
! 1682: frame++;
! 1683: } while (frame <= frameCount && !fastForward);
! 1684: overlayDisplayBuffer(toBuf, NULL);
! 1685: }
! 1686:
! 1687: // takes dungeon coordinates
! 1688: void colorBlendCell(short x, short y, color *hiliteColor, short hiliteStrength) {
! 1689: enum displayGlyph displayChar;
! 1690: color foreColor, backColor;
! 1691:
! 1692: getCellAppearance(x, y, &displayChar, &foreColor, &backColor);
! 1693: applyColorAverage(&foreColor, hiliteColor, hiliteStrength);
! 1694: applyColorAverage(&backColor, hiliteColor, hiliteStrength);
! 1695: plotCharWithColor(displayChar, mapToWindowX(x), mapToWindowY(y), &foreColor, &backColor);
! 1696: }
! 1697:
! 1698: // takes dungeon coordinates
! 1699: void hiliteCell(short x, short y, const color *hiliteColor, short hiliteStrength, boolean distinctColors) {
! 1700: enum displayGlyph displayChar;
! 1701: color foreColor, backColor;
! 1702:
! 1703: assureCosmeticRNG;
! 1704:
! 1705: getCellAppearance(x, y, &displayChar, &foreColor, &backColor);
! 1706: applyColorAugment(&foreColor, hiliteColor, hiliteStrength);
! 1707: applyColorAugment(&backColor, hiliteColor, hiliteStrength);
! 1708: if (distinctColors) {
! 1709: separateColors(&foreColor, &backColor);
! 1710: }
! 1711: plotCharWithColor(displayChar, mapToWindowX(x), mapToWindowY(y), &foreColor, &backColor);
! 1712:
! 1713: restoreRNG;
! 1714: }
! 1715:
! 1716: short adjustedLightValue(short x) {
! 1717: if (x <= LIGHT_SMOOTHING_THRESHOLD) {
! 1718: return x;
! 1719: } else {
! 1720: return fp_sqrt(x * FP_FACTOR / LIGHT_SMOOTHING_THRESHOLD) * LIGHT_SMOOTHING_THRESHOLD / FP_FACTOR;
! 1721: }
! 1722: }
! 1723:
! 1724: void colorMultiplierFromDungeonLight(short x, short y, color *editColor) {
! 1725:
! 1726: editColor->red = editColor->redRand = adjustedLightValue(max(0, tmap[x][y].light[0]));
! 1727: editColor->green = editColor->greenRand = adjustedLightValue(max(0, tmap[x][y].light[1]));
! 1728: editColor->blue = editColor->blueRand = adjustedLightValue(max(0, tmap[x][y].light[2]));
! 1729:
! 1730: editColor->rand = adjustedLightValue(max(0, tmap[x][y].light[0] + tmap[x][y].light[1] + tmap[x][y].light[2]) / 3);
! 1731: editColor->colorDances = false;
! 1732: }
! 1733:
! 1734: void plotCharWithColor(enum displayGlyph inputChar, short xLoc, short yLoc, const color *cellForeColor, const color *cellBackColor) {
! 1735: short oldRNG;
! 1736:
! 1737: short foreRed = cellForeColor->red,
! 1738: foreGreen = cellForeColor->green,
! 1739: foreBlue = cellForeColor->blue,
! 1740:
! 1741: backRed = cellBackColor->red,
! 1742: backGreen = cellBackColor->green,
! 1743: backBlue = cellBackColor->blue,
! 1744:
! 1745: foreRand, backRand;
! 1746:
! 1747: brogueAssert(coordinatesAreInWindow(xLoc, yLoc));
! 1748:
! 1749: if (rogue.gameHasEnded || rogue.playbackFastForward) {
! 1750: return;
! 1751: }
! 1752:
! 1753: //assureCosmeticRNG;
! 1754: oldRNG = rogue.RNG;
! 1755: rogue.RNG = RNG_COSMETIC;
! 1756:
! 1757: foreRand = rand_range(0, cellForeColor->rand);
! 1758: backRand = rand_range(0, cellBackColor->rand);
! 1759: foreRed += rand_range(0, cellForeColor->redRand) + foreRand;
! 1760: foreGreen += rand_range(0, cellForeColor->greenRand) + foreRand;
! 1761: foreBlue += rand_range(0, cellForeColor->blueRand) + foreRand;
! 1762: backRed += rand_range(0, cellBackColor->redRand) + backRand;
! 1763: backGreen += rand_range(0, cellBackColor->greenRand) + backRand;
! 1764: backBlue += rand_range(0, cellBackColor->blueRand) + backRand;
! 1765:
! 1766: foreRed = min(100, max(0, foreRed));
! 1767: foreGreen = min(100, max(0, foreGreen));
! 1768: foreBlue = min(100, max(0, foreBlue));
! 1769: backRed = min(100, max(0, backRed));
! 1770: backGreen = min(100, max(0, backGreen));
! 1771: backBlue = min(100, max(0, backBlue));
! 1772:
! 1773: if (inputChar != ' '
! 1774: && foreRed == backRed
! 1775: && foreGreen == backGreen
! 1776: && foreBlue == backBlue) {
! 1777:
! 1778: inputChar = ' ';
! 1779: }
! 1780:
! 1781: if (inputChar != displayBuffer[xLoc][yLoc].character
! 1782: || foreRed != displayBuffer[xLoc][yLoc].foreColorComponents[0]
! 1783: || foreGreen != displayBuffer[xLoc][yLoc].foreColorComponents[1]
! 1784: || foreBlue != displayBuffer[xLoc][yLoc].foreColorComponents[2]
! 1785: || backRed != displayBuffer[xLoc][yLoc].backColorComponents[0]
! 1786: || backGreen != displayBuffer[xLoc][yLoc].backColorComponents[1]
! 1787: || backBlue != displayBuffer[xLoc][yLoc].backColorComponents[2]) {
! 1788:
! 1789: displayBuffer[xLoc][yLoc].needsUpdate = true;
! 1790:
! 1791: displayBuffer[xLoc][yLoc].character = inputChar;
! 1792: displayBuffer[xLoc][yLoc].foreColorComponents[0] = foreRed;
! 1793: displayBuffer[xLoc][yLoc].foreColorComponents[1] = foreGreen;
! 1794: displayBuffer[xLoc][yLoc].foreColorComponents[2] = foreBlue;
! 1795: displayBuffer[xLoc][yLoc].backColorComponents[0] = backRed;
! 1796: displayBuffer[xLoc][yLoc].backColorComponents[1] = backGreen;
! 1797: displayBuffer[xLoc][yLoc].backColorComponents[2] = backBlue;
! 1798: }
! 1799:
! 1800: restoreRNG;
! 1801: }
! 1802:
! 1803: void plotCharToBuffer(enum displayGlyph inputChar, short x, short y, color *foreColor, color *backColor, cellDisplayBuffer dbuf[COLS][ROWS]) {
! 1804: short oldRNG;
! 1805:
! 1806: if (!dbuf) {
! 1807: plotCharWithColor(inputChar, x, y, foreColor, backColor);
! 1808: return;
! 1809: }
! 1810:
! 1811: brogueAssert(coordinatesAreInWindow(x, y));
! 1812:
! 1813: oldRNG = rogue.RNG;
! 1814: rogue.RNG = RNG_COSMETIC;
! 1815: //assureCosmeticRNG;
! 1816: dbuf[x][y].foreColorComponents[0] = foreColor->red + rand_range(0, foreColor->redRand) + rand_range(0, foreColor->rand);
! 1817: dbuf[x][y].foreColorComponents[1] = foreColor->green + rand_range(0, foreColor->greenRand) + rand_range(0, foreColor->rand);
! 1818: dbuf[x][y].foreColorComponents[2] = foreColor->blue + rand_range(0, foreColor->blueRand) + rand_range(0, foreColor->rand);
! 1819: dbuf[x][y].backColorComponents[0] = backColor->red + rand_range(0, backColor->redRand) + rand_range(0, backColor->rand);
! 1820: dbuf[x][y].backColorComponents[1] = backColor->green + rand_range(0, backColor->greenRand) + rand_range(0, backColor->rand);
! 1821: dbuf[x][y].backColorComponents[2] = backColor->blue + rand_range(0, backColor->blueRand) + rand_range(0, backColor->rand);
! 1822: dbuf[x][y].character = inputChar;
! 1823: dbuf[x][y].opacity = 100;
! 1824: restoreRNG;
! 1825: }
! 1826:
! 1827: void plotForegroundChar(enum displayGlyph inputChar, short x, short y, color *foreColor, boolean affectedByLighting) {
! 1828: color multColor, myColor, backColor, ignoredColor;
! 1829: enum displayGlyph ignoredChar;
! 1830:
! 1831: myColor = *foreColor;
! 1832: getCellAppearance(x, y, &ignoredChar, &ignoredColor, &backColor);
! 1833: if (affectedByLighting) {
! 1834: colorMultiplierFromDungeonLight(x, y, &multColor);
! 1835: applyColorMultiplier(&myColor, &multColor);
! 1836: }
! 1837: plotCharWithColor(inputChar, mapToWindowX(x), mapToWindowY(y), &myColor, &backColor);
! 1838: }
! 1839:
! 1840: // Set to false and draws don't take effect, they simply queue up. Set to true and all of the
! 1841: // queued up draws take effect.
! 1842: void commitDraws() {
! 1843: short i, j;
! 1844:
! 1845: for (j=0; j<ROWS; j++) {
! 1846: for (i=0; i<COLS; i++) {
! 1847: if (displayBuffer[i][j].needsUpdate) {
! 1848: plotChar(displayBuffer[i][j].character, i, j,
! 1849: displayBuffer[i][j].foreColorComponents[0],
! 1850: displayBuffer[i][j].foreColorComponents[1],
! 1851: displayBuffer[i][j].foreColorComponents[2],
! 1852: displayBuffer[i][j].backColorComponents[0],
! 1853: displayBuffer[i][j].backColorComponents[1],
! 1854: displayBuffer[i][j].backColorComponents[2]);
! 1855: displayBuffer[i][j].needsUpdate = false;
! 1856: }
! 1857: }
! 1858: }
! 1859: }
! 1860:
! 1861: // Debug feature: display the level to the screen without regard to lighting, field of view, etc.
! 1862: void dumpLevelToScreen() {
! 1863: short i, j;
! 1864: pcell backup;
! 1865:
! 1866: assureCosmeticRNG;
! 1867: for (i=0; i<DCOLS; i++) {
! 1868: for (j=0; j<DROWS; j++) {
! 1869: if (pmap[i][j].layers[DUNGEON] != GRANITE
! 1870: || (pmap[i][j].flags & DISCOVERED)) {
! 1871:
! 1872: backup = pmap[i][j];
! 1873: pmap[i][j].flags |= (VISIBLE | DISCOVERED);
! 1874: tmap[i][j].light[0] = 100;
! 1875: tmap[i][j].light[1] = 100;
! 1876: tmap[i][j].light[2] = 100;
! 1877: refreshDungeonCell(i, j);
! 1878: pmap[i][j] = backup;
! 1879: } else {
! 1880: plotCharWithColor(' ', mapToWindowX(i), mapToWindowY(j), &white, &black);
! 1881: }
! 1882:
! 1883: }
! 1884: }
! 1885: restoreRNG;
! 1886: }
! 1887:
! 1888: // To be used immediately after dumpLevelToScreen() above.
! 1889: // Highlight the portion indicated by hiliteCharGrid with the hiliteColor at the hiliteStrength -- both latter arguments are optional.
! 1890: void hiliteCharGrid(char hiliteCharGrid[DCOLS][DROWS], color *hiliteColor, short hiliteStrength) {
! 1891: short i, j, x, y;
! 1892: color hCol;
! 1893:
! 1894: assureCosmeticRNG;
! 1895:
! 1896: if (hiliteColor) {
! 1897: hCol = *hiliteColor;
! 1898: } else {
! 1899: hCol = yellow;
! 1900: }
! 1901:
! 1902: bakeColor(&hCol);
! 1903:
! 1904: if (!hiliteStrength) {
! 1905: hiliteStrength = 75;
! 1906: }
! 1907:
! 1908: for (i=0; i<DCOLS; i++) {
! 1909: for (j=0; j<DROWS; j++) {
! 1910: if (hiliteCharGrid[i][j]) {
! 1911: x = mapToWindowX(i);
! 1912: y = mapToWindowY(j);
! 1913:
! 1914: displayBuffer[x][y].needsUpdate = true;
! 1915: displayBuffer[x][y].backColorComponents[0] = clamp(displayBuffer[x][y].backColorComponents[0] + hCol.red * hiliteStrength / 100, 0, 100);
! 1916: displayBuffer[x][y].backColorComponents[1] = clamp(displayBuffer[x][y].backColorComponents[1] + hCol.green * hiliteStrength / 100, 0, 100);
! 1917: displayBuffer[x][y].backColorComponents[2] = clamp(displayBuffer[x][y].backColorComponents[2] + hCol.blue * hiliteStrength / 100, 0, 100);
! 1918: displayBuffer[x][y].foreColorComponents[0] = clamp(displayBuffer[x][y].foreColorComponents[0] + hCol.red * hiliteStrength / 100, 0, 100);
! 1919: displayBuffer[x][y].foreColorComponents[1] = clamp(displayBuffer[x][y].foreColorComponents[1] + hCol.green * hiliteStrength / 100, 0, 100);
! 1920: displayBuffer[x][y].foreColorComponents[2] = clamp(displayBuffer[x][y].foreColorComponents[2] + hCol.blue * hiliteStrength / 100, 0, 100);
! 1921: }
! 1922: }
! 1923: }
! 1924: restoreRNG;
! 1925: }
! 1926:
! 1927: void blackOutScreen() {
! 1928: short i, j;
! 1929:
! 1930: for (i=0; i<COLS; i++) {
! 1931: for (j=0; j<ROWS; j++) {
! 1932: plotCharWithColor(' ', i, j, &black, &black);
! 1933: }
! 1934: }
! 1935: }
! 1936:
! 1937: void colorOverDungeon(const color *color) {
! 1938: short i, j;
! 1939:
! 1940: for (i=0; i<DCOLS; i++) {
! 1941: for (j=0; j<DROWS; j++) {
! 1942: plotCharWithColor(' ', mapToWindowX(i), mapToWindowY(j), color, color);
! 1943: }
! 1944: }
! 1945: }
! 1946:
! 1947: void copyDisplayBuffer(cellDisplayBuffer toBuf[COLS][ROWS], cellDisplayBuffer fromBuf[COLS][ROWS]) {
! 1948: short i, j;
! 1949:
! 1950: for (i=0; i<COLS; i++) {
! 1951: for (j=0; j<ROWS; j++) {
! 1952: toBuf[i][j] = fromBuf[i][j];
! 1953: }
! 1954: }
! 1955: }
! 1956:
! 1957: void clearDisplayBuffer(cellDisplayBuffer dbuf[COLS][ROWS]) {
! 1958: short i, j, k;
! 1959:
! 1960: for (i=0; i<COLS; i++) {
! 1961: for (j=0; j<ROWS; j++) {
! 1962: dbuf[i][j].character = ' ';
! 1963: for (k=0; k<3; k++) {
! 1964: dbuf[i][j].foreColorComponents[k] = 0;
! 1965: dbuf[i][j].backColorComponents[k] = 0;
! 1966: }
! 1967: dbuf[i][j].opacity = 0;
! 1968: }
! 1969: }
! 1970: }
! 1971:
! 1972: color colorFromComponents(char rgb[3]) {
! 1973: color theColor = black;
! 1974: theColor.red = rgb[0];
! 1975: theColor.green = rgb[1];
! 1976: theColor.blue = rgb[2];
! 1977: return theColor;
! 1978: }
! 1979:
! 1980: // draws overBuf over the current display with per-cell pseudotransparency as specified in overBuf.
! 1981: // If previousBuf is not null, it gets filled with the preexisting display for reversion purposes.
! 1982: void overlayDisplayBuffer(cellDisplayBuffer overBuf[COLS][ROWS], cellDisplayBuffer previousBuf[COLS][ROWS]) {
! 1983: short i, j;
! 1984: color foreColor, backColor, tempColor;
! 1985: enum displayGlyph character;
! 1986:
! 1987: if (previousBuf) {
! 1988: copyDisplayBuffer(previousBuf, displayBuffer);
! 1989: }
! 1990:
! 1991: for (i=0; i<COLS; i++) {
! 1992: for (j=0; j<ROWS; j++) {
! 1993:
! 1994: if (overBuf[i][j].opacity != 0) {
! 1995: backColor = colorFromComponents(overBuf[i][j].backColorComponents);
! 1996:
! 1997: // character and fore color:
! 1998: if (overBuf[i][j].character == ' ') { // Blank cells in the overbuf take the character from the screen.
! 1999: character = displayBuffer[i][j].character;
! 2000: foreColor = colorFromComponents(displayBuffer[i][j].foreColorComponents);
! 2001: applyColorAverage(&foreColor, &backColor, overBuf[i][j].opacity);
! 2002: } else {
! 2003: character = overBuf[i][j].character;
! 2004: foreColor = colorFromComponents(overBuf[i][j].foreColorComponents);
! 2005: }
! 2006:
! 2007: // back color:
! 2008: tempColor = colorFromComponents(displayBuffer[i][j].backColorComponents);
! 2009: applyColorAverage(&backColor, &tempColor, 100 - overBuf[i][j].opacity);
! 2010:
! 2011: plotCharWithColor(character, i, j, &foreColor, &backColor);
! 2012: }
! 2013: }
! 2014: }
! 2015: }
! 2016:
! 2017: // Takes a list of locations, a color and a list of strengths and flashes the foregrounds of those locations.
! 2018: // Strengths are percentages measuring how hard the color flashes at its peak.
! 2019: void flashForeground(short *x, short *y, color **flashColor, short *flashStrength, short count, short frames) {
! 2020: short i, j, percent;
! 2021: enum displayGlyph *displayChar;
! 2022: color *bColor, *fColor, newColor;
! 2023: short oldRNG;
! 2024:
! 2025: if (count <= 0) {
! 2026: return;
! 2027: }
! 2028:
! 2029: oldRNG = rogue.RNG;
! 2030: rogue.RNG = RNG_COSMETIC;
! 2031: //assureCosmeticRNG;
! 2032:
! 2033: displayChar = (enum displayGlyph *) malloc(count * sizeof(enum displayGlyph));
! 2034: fColor = (color *) malloc(count * sizeof(color));
! 2035: bColor = (color *) malloc(count * sizeof(color));
! 2036:
! 2037: for (i=0; i<count; i++) {
! 2038: getCellAppearance(x[i], y[i], &displayChar[i], &fColor[i], &bColor[i]);
! 2039: bakeColor(&fColor[i]);
! 2040: bakeColor(&bColor[i]);
! 2041: }
! 2042:
! 2043: for (j=frames; j>= 0; j--) {
! 2044: for (i=0; i<count; i++) {
! 2045: percent = flashStrength[i] * j / frames;
! 2046: newColor = fColor[i];
! 2047: applyColorAverage(&newColor, flashColor[i], percent);
! 2048: plotCharWithColor(displayChar[i], mapToWindowX(x[i]), mapToWindowY(y[i]), &newColor, &(bColor[i]));
! 2049: }
! 2050: if (j) {
! 2051: if (pauseBrogue(16)) {
! 2052: j = 1;
! 2053: }
! 2054: }
! 2055: }
! 2056:
! 2057: free(displayChar);
! 2058: free(fColor);
! 2059: free(bColor);
! 2060:
! 2061: restoreRNG;
! 2062: }
! 2063:
! 2064: void flashCell(color *theColor, short frames, short x, short y) {
! 2065: short i;
! 2066: boolean interrupted = false;
! 2067:
! 2068: for (i=0; i<frames && !interrupted; i++) {
! 2069: colorBlendCell(x, y, theColor, 100 - 100 * i / frames);
! 2070: interrupted = pauseBrogue(50);
! 2071: }
! 2072:
! 2073: refreshDungeonCell(x, y);
! 2074: }
! 2075:
! 2076: // special effect expanding flash of light at dungeon coordinates (x, y) restricted to tiles with matching flags
! 2077: void colorFlash(const color *theColor, unsigned long reqTerrainFlags,
! 2078: unsigned long reqTileFlags, short frames, short maxRadius, short x, short y) {
! 2079: short i, j, k, intensity, currentRadius, fadeOut;
! 2080: short localRadius[DCOLS][DROWS];
! 2081: boolean tileQualifies[DCOLS][DROWS], aTileQualified, fastForward;
! 2082:
! 2083: aTileQualified = false;
! 2084: fastForward = false;
! 2085:
! 2086: for (i = max(x - maxRadius, 0); i <= min(x + maxRadius, DCOLS - 1); i++) {
! 2087: for (j = max(y - maxRadius, 0); j <= min(y + maxRadius, DROWS - 1); j++) {
! 2088: if ((!reqTerrainFlags || cellHasTerrainFlag(reqTerrainFlags, i, j))
! 2089: && (!reqTileFlags || (pmap[i][j].flags & reqTileFlags))
! 2090: && (i-x) * (i-x) + (j-y) * (j-y) <= maxRadius * maxRadius) {
! 2091:
! 2092: tileQualifies[i][j] = true;
! 2093: localRadius[i][j] = fp_sqrt(((i-x) * (i-x) + (j-y) * (j-y)) * FP_FACTOR) / FP_FACTOR;
! 2094: aTileQualified = true;
! 2095: } else {
! 2096: tileQualifies[i][j] = false;
! 2097: }
! 2098: }
! 2099: }
! 2100:
! 2101: if (!aTileQualified) {
! 2102: return;
! 2103: }
! 2104:
! 2105: for (k = 1; k <= frames; k++) {
! 2106: currentRadius = max(1, maxRadius * k / frames);
! 2107: fadeOut = min(100, (frames - k) * 100 * 5 / frames);
! 2108: for (i = max(x - maxRadius, 0); i <= min(x + maxRadius, DCOLS - 1); i++) {
! 2109: for (j = max(y - maxRadius, 0); j <= min(y + maxRadius, DROWS - 1); j++) {
! 2110: if (tileQualifies[i][j] && (localRadius[i][j] <= currentRadius)) {
! 2111:
! 2112: intensity = 100 - 100 * (currentRadius - localRadius[i][j] - 2) / currentRadius;
! 2113: intensity = fadeOut * intensity / 100;
! 2114:
! 2115: hiliteCell(i, j, theColor, intensity, false);
! 2116: }
! 2117: }
! 2118: }
! 2119: if (!fastForward && (rogue.playbackFastForward || pauseBrogue(50))) {
! 2120: k = frames - 1;
! 2121: fastForward = true;
! 2122: }
! 2123: }
! 2124: }
! 2125:
! 2126: #define bCurve(x) (((x) * (x) + 11) / (10 * ((x) * (x) + 1)) - 0.1)
! 2127:
! 2128: // x and y are global coordinates, not within the playing square
! 2129: void funkyFade(cellDisplayBuffer displayBuf[COLS][ROWS], const color *colorStart,
! 2130: const color *colorEnd, short stepCount, short x, short y, boolean invert) {
! 2131: short i, j, n, weight;
! 2132: double x2, y2, weightGrid[COLS][ROWS][3], percentComplete;
! 2133: color tempColor, colorMid, foreColor, backColor;
! 2134: enum displayGlyph tempChar;
! 2135: short **distanceMap;
! 2136: boolean fastForward;
! 2137:
! 2138: assureCosmeticRNG;
! 2139:
! 2140: fastForward = false;
! 2141: distanceMap = allocGrid();
! 2142: fillGrid(distanceMap, 0);
! 2143: calculateDistances(distanceMap, player.xLoc, player.yLoc, T_OBSTRUCTS_PASSABILITY, 0, true, true);
! 2144:
! 2145: for (i=0; i<COLS; i++) {
! 2146: x2 = (double) ((i - x) * 5.0 / COLS);
! 2147: for (j=0; j<ROWS; j++) {
! 2148: y2 = (double) ((j - y) * 2.5 / ROWS);
! 2149:
! 2150: weightGrid[i][j][0] = bCurve(x2*x2+y2*y2) * (.7 + .3 * cos(5*x2*x2) * cos(5*y2*y2));
! 2151: weightGrid[i][j][1] = bCurve(x2*x2+y2*y2) * (.7 + .3 * sin(5*x2*x2) * cos(5*y2*y2));
! 2152: weightGrid[i][j][2] = bCurve(x2*x2+y2*y2);
! 2153: }
! 2154: }
! 2155:
! 2156: for (n=(invert ? stepCount - 1 : 0); (invert ? n >= 0 : n <= stepCount); n += (invert ? -1 : 1)) {
! 2157: for (i=0; i<COLS; i++) {
! 2158: for (j=0; j<ROWS; j++) {
! 2159:
! 2160: percentComplete = (double) (n) * 100 / stepCount;
! 2161:
! 2162: colorMid = *colorStart;
! 2163: if (colorEnd) {
! 2164: applyColorAverage(&colorMid, colorEnd, n * 100 / stepCount);
! 2165: }
! 2166:
! 2167: // the fade color floods the reachable dungeon tiles faster
! 2168: if (!invert && coordinatesAreInMap(windowToMapX(i), windowToMapY(j))
! 2169: && distanceMap[windowToMapX(i)][windowToMapY(j)] >= 0 && distanceMap[windowToMapX(i)][windowToMapY(j)] < 30000) {
! 2170: percentComplete *= 1.0 + (100.0 - min(100, distanceMap[windowToMapX(i)][windowToMapY(j)])) / 100.;
! 2171: }
! 2172:
! 2173: weight = (short)(percentComplete + weightGrid[i][j][2] * percentComplete * 10);
! 2174: weight = min(100, weight);
! 2175: tempColor = black;
! 2176:
! 2177: tempColor.red = (short)(percentComplete + weightGrid[i][j][0] * percentComplete * 10) * colorMid.red / 100;
! 2178: tempColor.red = min(colorMid.red, tempColor.red);
! 2179:
! 2180: tempColor.green = (short)(percentComplete + weightGrid[i][j][1] * percentComplete * 10) * colorMid.green / 100;
! 2181: tempColor.green = min(colorMid.green, tempColor.green);
! 2182:
! 2183: tempColor.blue = (short)(percentComplete + weightGrid[i][j][2] * percentComplete * 10) * colorMid.blue / 100;
! 2184: tempColor.blue = min(colorMid.blue, tempColor.blue);
! 2185:
! 2186: backColor = black;
! 2187:
! 2188: backColor.red = displayBuf[i][j].backColorComponents[0];
! 2189: backColor.green = displayBuf[i][j].backColorComponents[1];
! 2190: backColor.blue = displayBuf[i][j].backColorComponents[2];
! 2191:
! 2192: foreColor = (invert ? white : black);
! 2193:
! 2194: if (j < MESSAGE_LINES
! 2195: && i >= mapToWindowX(0)
! 2196: && i < mapToWindowX(strLenWithoutEscapes(displayedMessage[MESSAGE_LINES - j - 1]))) {
! 2197: tempChar = displayedMessage[MESSAGE_LINES - j - 1][windowToMapX(i)];
! 2198: } else {
! 2199: tempChar = displayBuf[i][j].character;
! 2200:
! 2201: foreColor.red = displayBuf[i][j].foreColorComponents[0];
! 2202: foreColor.green = displayBuf[i][j].foreColorComponents[1];
! 2203: foreColor.blue = displayBuf[i][j].foreColorComponents[2];
! 2204:
! 2205: applyColorAverage(&foreColor, &tempColor, weight);
! 2206: }
! 2207: applyColorAverage(&backColor, &tempColor, weight);
! 2208: plotCharWithColor(tempChar, i, j, &foreColor, &backColor);
! 2209: }
! 2210: }
! 2211: if (!fastForward && pauseBrogue(16)) {
! 2212: // drop the event - skipping the transition should only skip the transition
! 2213: rogueEvent event;
! 2214: nextKeyOrMouseEvent(&event, false, false);
! 2215: fastForward = true;
! 2216: n = (invert ? 1 : stepCount - 2);
! 2217: }
! 2218: }
! 2219:
! 2220: freeGrid(distanceMap);
! 2221:
! 2222: restoreRNG;
! 2223: }
! 2224:
! 2225: void displayWaypoints() {
! 2226: short i, j, w, lowestDistance;
! 2227:
! 2228: for (i=0; i<DCOLS; i++) {
! 2229: for (j=0; j<DROWS; j++) {
! 2230: lowestDistance = 30000;
! 2231: for (w=0; w<rogue.wpCount; w++) {
! 2232: if (rogue.wpDistance[w][i][j] < lowestDistance) {
! 2233: lowestDistance = rogue.wpDistance[w][i][j];
! 2234: }
! 2235: }
! 2236: if (lowestDistance < 10) {
! 2237: hiliteCell(i, j, &white, clamp(100 - lowestDistance*15, 0, 100), true);
! 2238: }
! 2239: }
! 2240: }
! 2241: temporaryMessage("Waypoints:", true);
! 2242: }
! 2243:
! 2244: void displayMachines() {
! 2245: short i, j;
! 2246: color foreColor, backColor, machineColors[50];
! 2247: enum displayGlyph dchar;
! 2248:
! 2249: assureCosmeticRNG;
! 2250:
! 2251: for (i=0; i<50; i++) {
! 2252: machineColors[i] = black;
! 2253: machineColors[i].red = rand_range(0, 100);
! 2254: machineColors[i].green = rand_range(0, 100);
! 2255: machineColors[i].blue = rand_range(0, 100);
! 2256: }
! 2257:
! 2258: for (i=0; i<DCOLS; i++) {
! 2259: for (j=0; j<DROWS; j++) {
! 2260: if (pmap[i][j].machineNumber) {
! 2261: getCellAppearance(i, j, &dchar, &foreColor, &backColor);
! 2262: applyColorAugment(&backColor, &(machineColors[pmap[i][j].machineNumber]), 50);
! 2263: //plotCharWithColor(dchar, mapToWindowX(i), mapToWindowY(j), &foreColor, &backColor);
! 2264: if (pmap[i][j].machineNumber < 10) {
! 2265: dchar ='0' + pmap[i][j].machineNumber;
! 2266: } else if (pmap[i][j].machineNumber < 10 + 26) {
! 2267: dchar = 'a' + pmap[i][j].machineNumber - 10;
! 2268: } else {
! 2269: dchar = 'A' + pmap[i][j].machineNumber - 10 - 26;
! 2270: }
! 2271: plotCharWithColor(dchar, mapToWindowX(i), mapToWindowY(j), &foreColor, &backColor);
! 2272: }
! 2273: }
! 2274: }
! 2275: displayMoreSign();
! 2276: displayLevel();
! 2277:
! 2278: restoreRNG;
! 2279: }
! 2280:
! 2281: #define CHOKEMAP_DISPLAY_CUTOFF 160
! 2282: void displayChokeMap() {
! 2283: short i, j;
! 2284: color foreColor, backColor;
! 2285: enum displayGlyph dchar;
! 2286:
! 2287: for (i=0; i<DCOLS; i++) {
! 2288: for (j=0; j<DROWS; j++) {
! 2289: if (chokeMap[i][j] < CHOKEMAP_DISPLAY_CUTOFF) {
! 2290: if (pmap[i][j].flags & IS_GATE_SITE) {
! 2291: getCellAppearance(i, j, &dchar, &foreColor, &backColor);
! 2292: applyColorAugment(&backColor, &teal, 50);
! 2293: plotCharWithColor(dchar, mapToWindowX(i), mapToWindowY(j), &foreColor, &backColor);
! 2294: } else
! 2295: if (chokeMap[i][j] < CHOKEMAP_DISPLAY_CUTOFF) {
! 2296: getCellAppearance(i, j, &dchar, &foreColor, &backColor);
! 2297: applyColorAugment(&backColor, &red, 100 - chokeMap[i][j] * 100 / CHOKEMAP_DISPLAY_CUTOFF);
! 2298: plotCharWithColor(dchar, mapToWindowX(i), mapToWindowY(j), &foreColor, &backColor);
! 2299: }
! 2300: }
! 2301: }
! 2302: }
! 2303: displayMoreSign();
! 2304: displayLevel();
! 2305: }
! 2306:
! 2307: void displayLoops() {
! 2308: short i, j;
! 2309: color foreColor, backColor;
! 2310: enum displayGlyph dchar;
! 2311:
! 2312: for (i=0; i<DCOLS; i++) {
! 2313: for (j=0; j<DROWS; j++) {
! 2314: if (pmap[i][j].flags & IN_LOOP) {
! 2315: getCellAppearance(i, j, &dchar, &foreColor, &backColor);
! 2316: applyColorAugment(&backColor, &yellow, 50);
! 2317: plotCharWithColor(dchar, mapToWindowX(i), mapToWindowY(j), &foreColor, &backColor);
! 2318: //colorBlendCell(i, j, &tempColor, 100);//hiliteCell(i, j, &tempColor, 100, true);
! 2319: }
! 2320: if (pmap[i][j].flags & IS_CHOKEPOINT) {
! 2321: getCellAppearance(i, j, &dchar, &foreColor, &backColor);
! 2322: applyColorAugment(&backColor, &teal, 50);
! 2323: plotCharWithColor(dchar, mapToWindowX(i), mapToWindowY(j), &foreColor, &backColor);
! 2324: }
! 2325: }
! 2326: }
! 2327: waitForAcknowledgment();
! 2328: }
! 2329:
! 2330: void exploreKey(const boolean controlKey) {
! 2331: short x, y, finalX = 0, finalY = 0;
! 2332: short **exploreMap;
! 2333: enum directions dir;
! 2334: boolean tooDark = false;
! 2335:
! 2336: // fight any adjacent enemies first
! 2337: dir = adjacentFightingDir();
! 2338: if (dir == NO_DIRECTION) {
! 2339: for (dir = 0; dir < DIRECTION_COUNT; dir++) {
! 2340: x = player.xLoc + nbDirs[dir][0];
! 2341: y = player.yLoc + nbDirs[dir][1];
! 2342: if (coordinatesAreInMap(x, y)
! 2343: && !(pmap[x][y].flags & DISCOVERED)) {
! 2344:
! 2345: tooDark = true;
! 2346: break;
! 2347: }
! 2348: }
! 2349: if (!tooDark) {
! 2350: x = finalX = player.xLoc;
! 2351: y = finalY = player.yLoc;
! 2352:
! 2353: exploreMap = allocGrid();
! 2354: getExploreMap(exploreMap, false);
! 2355: do {
! 2356: dir = nextStep(exploreMap, x, y, NULL, false);
! 2357: if (dir != NO_DIRECTION) {
! 2358: x += nbDirs[dir][0];
! 2359: y += nbDirs[dir][1];
! 2360: if (pmap[x][y].flags & (DISCOVERED | MAGIC_MAPPED)) {
! 2361: finalX = x;
! 2362: finalY = y;
! 2363: }
! 2364: }
! 2365: } while (dir != NO_DIRECTION);
! 2366: freeGrid(exploreMap);
! 2367: }
! 2368: } else {
! 2369: x = finalX = player.xLoc + nbDirs[dir][0];
! 2370: y = finalY = player.yLoc + nbDirs[dir][1];
! 2371: }
! 2372:
! 2373: if (tooDark) {
! 2374: message("It's too dark to explore!", false);
! 2375: } else if (x == player.xLoc && y == player.yLoc) {
! 2376: message("I see no path for further exploration.", false);
! 2377: } else if (proposeOrConfirmLocation(finalX, finalY, "I see no path for further exploration.")) {
! 2378: explore(controlKey ? 1 : 20); // Do the exploring until interrupted.
! 2379: hideCursor();
! 2380: exploreKey(controlKey);
! 2381: }
! 2382: }
! 2383:
! 2384: boolean pauseBrogue(short milliseconds) {
! 2385: boolean interrupted;
! 2386:
! 2387: commitDraws();
! 2388: if (rogue.playbackMode && rogue.playbackFastForward) {
! 2389: interrupted = true;
! 2390: } else {
! 2391: interrupted = pauseForMilliseconds(milliseconds);
! 2392: }
! 2393: return interrupted;
! 2394: }
! 2395:
! 2396: void nextBrogueEvent(rogueEvent *returnEvent, boolean textInput, boolean colorsDance, boolean realInputEvenInPlayback) {
! 2397: rogueEvent recordingInput;
! 2398: boolean repeatAgain, interaction;
! 2399: short pauseDuration;
! 2400:
! 2401: returnEvent->eventType = EVENT_ERROR;
! 2402:
! 2403: if (rogue.playbackMode && !realInputEvenInPlayback) {
! 2404: do {
! 2405: repeatAgain = false;
! 2406: if ((!rogue.playbackFastForward && rogue.playbackBetweenTurns)
! 2407: || rogue.playbackOOS) {
! 2408:
! 2409: pauseDuration = (rogue.playbackPaused ? DEFAULT_PLAYBACK_DELAY : rogue.playbackDelayThisTurn);
! 2410: if (pauseDuration && pauseBrogue(pauseDuration)) {
! 2411: // if the player did something during playback
! 2412: nextBrogueEvent(&recordingInput, false, false, true);
! 2413: interaction = executePlaybackInput(&recordingInput);
! 2414: repeatAgain = !rogue.playbackPaused && interaction;
! 2415: }
! 2416: }
! 2417: } while ((repeatAgain || rogue.playbackOOS) && !rogue.gameHasEnded);
! 2418: rogue.playbackDelayThisTurn = rogue.playbackDelayPerTurn;
! 2419: recallEvent(returnEvent);
! 2420: } else {
! 2421: commitDraws();
! 2422: if (rogue.creaturesWillFlashThisTurn) {
! 2423: displayMonsterFlashes(true);
! 2424: }
! 2425: do {
! 2426: nextKeyOrMouseEvent(returnEvent, textInput, colorsDance); // No mouse clicks outside of the window will register.
! 2427: } while (returnEvent->eventType == MOUSE_UP && !coordinatesAreInWindow(returnEvent->param1, returnEvent->param2));
! 2428: // recording done elsewhere
! 2429: }
! 2430:
! 2431: if (returnEvent->eventType == EVENT_ERROR) {
! 2432: rogue.playbackPaused = rogue.playbackMode; // pause if replaying
! 2433: message("Event error!", true);
! 2434: }
! 2435: }
! 2436:
! 2437: void executeMouseClick(rogueEvent *theEvent) {
! 2438: short x, y;
! 2439: boolean autoConfirm;
! 2440: x = theEvent->param1;
! 2441: y = theEvent->param2;
! 2442: autoConfirm = theEvent->controlKey;
! 2443:
! 2444: if (theEvent->eventType == RIGHT_MOUSE_UP) {
! 2445: displayInventory(ALL_ITEMS, 0, 0, true, true);
! 2446: } else if (coordinatesAreInMap(windowToMapX(x), windowToMapY(y))) {
! 2447: if (autoConfirm) {
! 2448: travel(windowToMapX(x), windowToMapY(y), autoConfirm);
! 2449: } else {
! 2450: rogue.cursorLoc[0] = windowToMapX(x);
! 2451: rogue.cursorLoc[1] = windowToMapY(y);
! 2452: mainInputLoop();
! 2453: }
! 2454:
! 2455: } else if (windowToMapX(x) >= 0 && windowToMapX(x) < DCOLS && y >= 0 && y < MESSAGE_LINES) {
! 2456: // If the click location is in the message block, display the message archive.
! 2457: displayMessageArchive();
! 2458: }
! 2459: }
! 2460:
! 2461: void executeKeystroke(signed long keystroke, boolean controlKey, boolean shiftKey) {
! 2462: char path[BROGUE_FILENAME_MAX];
! 2463: short direction = -1;
! 2464:
! 2465: confirmMessages();
! 2466: stripShiftFromMovementKeystroke(&keystroke);
! 2467:
! 2468: switch (keystroke) {
! 2469: case UP_KEY:
! 2470: case UP_ARROW:
! 2471: case NUMPAD_8:
! 2472: direction = UP;
! 2473: break;
! 2474: case DOWN_KEY:
! 2475: case DOWN_ARROW:
! 2476: case NUMPAD_2:
! 2477: direction = DOWN;
! 2478: break;
! 2479: case LEFT_KEY:
! 2480: case LEFT_ARROW:
! 2481: case NUMPAD_4:
! 2482: direction = LEFT;
! 2483: break;
! 2484: case RIGHT_KEY:
! 2485: case RIGHT_ARROW:
! 2486: case NUMPAD_6:
! 2487: direction = RIGHT;
! 2488: break;
! 2489: case NUMPAD_7:
! 2490: case UPLEFT_KEY:
! 2491: direction = UPLEFT;
! 2492: break;
! 2493: case UPRIGHT_KEY:
! 2494: case NUMPAD_9:
! 2495: direction = UPRIGHT;
! 2496: break;
! 2497: case DOWNLEFT_KEY:
! 2498: case NUMPAD_1:
! 2499: direction = DOWNLEFT;
! 2500: break;
! 2501: case DOWNRIGHT_KEY:
! 2502: case NUMPAD_3:
! 2503: direction = DOWNRIGHT;
! 2504: break;
! 2505: case DESCEND_KEY:
! 2506: considerCautiousMode();
! 2507: if (D_WORMHOLING) {
! 2508: recordKeystroke(DESCEND_KEY, false, false);
! 2509: useStairs(1);
! 2510: } else if (proposeOrConfirmLocation(rogue.downLoc[0], rogue.downLoc[1], "I see no way down.")) {
! 2511: travel(rogue.downLoc[0], rogue.downLoc[1], true);
! 2512: }
! 2513: break;
! 2514: case ASCEND_KEY:
! 2515: considerCautiousMode();
! 2516: if (D_WORMHOLING) {
! 2517: recordKeystroke(ASCEND_KEY, false, false);
! 2518: useStairs(-1);
! 2519: } else if (proposeOrConfirmLocation(rogue.upLoc[0], rogue.upLoc[1], "I see no way up.")) {
! 2520: travel(rogue.upLoc[0], rogue.upLoc[1], true);
! 2521: }
! 2522: break;
! 2523: case RETURN_KEY:
! 2524: showCursor();
! 2525: break;
! 2526: case REST_KEY:
! 2527: case PERIOD_KEY:
! 2528: case NUMPAD_5:
! 2529: considerCautiousMode();
! 2530: rogue.justRested = true;
! 2531: recordKeystroke(REST_KEY, false, false);
! 2532: playerTurnEnded();
! 2533: break;
! 2534: case AUTO_REST_KEY:
! 2535: rogue.justRested = true;
! 2536: autoRest();
! 2537: break;
! 2538: case SEARCH_KEY:
! 2539: if (controlKey) {
! 2540: rogue.disturbed = false;
! 2541: rogue.automationActive = true;
! 2542: do {
! 2543: manualSearch();
! 2544: if (pauseBrogue(80)) {
! 2545: rogue.disturbed = true;
! 2546: }
! 2547: } while (player.status[STATUS_SEARCHING] < 5 && !rogue.disturbed);
! 2548: rogue.automationActive = false;
! 2549: } else {
! 2550: manualSearch();
! 2551: }
! 2552: break;
! 2553: case INVENTORY_KEY:
! 2554: displayInventory(ALL_ITEMS, 0, 0, true, true);
! 2555: break;
! 2556: case EQUIP_KEY:
! 2557: equip(NULL);
! 2558: break;
! 2559: case UNEQUIP_KEY:
! 2560: unequip(NULL);
! 2561: break;
! 2562: case DROP_KEY:
! 2563: drop(NULL);
! 2564: break;
! 2565: case APPLY_KEY:
! 2566: apply(NULL, true);
! 2567: break;
! 2568: case THROW_KEY:
! 2569: throwCommand(NULL, false);
! 2570: break;
! 2571: case RETHROW_KEY:
! 2572: if (rogue.lastItemThrown != NULL && itemIsCarried(rogue.lastItemThrown)) {
! 2573: throwCommand(rogue.lastItemThrown, true);
! 2574: }
! 2575: break;
! 2576: case RELABEL_KEY:
! 2577: relabel(NULL);
! 2578: break;
! 2579: case TRUE_COLORS_KEY:
! 2580: rogue.trueColorMode = !rogue.trueColorMode;
! 2581: displayLevel();
! 2582: refreshSideBar(-1, -1, false);
! 2583: if (rogue.trueColorMode) {
! 2584: messageWithColor(KEYBOARD_LABELS ? "Color effects disabled. Press '\\' again to enable." : "Color effects disabled.",
! 2585: &teal, false);
! 2586: } else {
! 2587: messageWithColor(KEYBOARD_LABELS ? "Color effects enabled. Press '\\' again to disable." : "Color effects enabled.",
! 2588: &teal, false);
! 2589: }
! 2590: break;
! 2591: case AGGRO_DISPLAY_KEY:
! 2592: rogue.displayAggroRangeMode = !rogue.displayAggroRangeMode;
! 2593: displayLevel();
! 2594: refreshSideBar(-1, -1, false);
! 2595: if (rogue.displayAggroRangeMode) {
! 2596: messageWithColor(KEYBOARD_LABELS ? "Stealth range displayed. Press ']' again to hide." : "Stealth range displayed.",
! 2597: &teal, false);
! 2598: } else {
! 2599: messageWithColor(KEYBOARD_LABELS ? "Stealth range hidden. Press ']' again to display." : "Stealth range hidden.",
! 2600: &teal, false);
! 2601: }
! 2602: break;
! 2603: case CALL_KEY:
! 2604: call(NULL);
! 2605: break;
! 2606: case EXPLORE_KEY:
! 2607: considerCautiousMode();
! 2608: exploreKey(controlKey);
! 2609: break;
! 2610: case AUTOPLAY_KEY:
! 2611: if (confirm("Turn on autopilot?", false)) {
! 2612: autoPlayLevel(controlKey);
! 2613: }
! 2614: break;
! 2615: case MESSAGE_ARCHIVE_KEY:
! 2616: displayMessageArchive();
! 2617: break;
! 2618: case BROGUE_HELP_KEY:
! 2619: printHelpScreen();
! 2620: break;
! 2621: case DISCOVERIES_KEY:
! 2622: printDiscoveriesScreen();
! 2623: break;
! 2624: case VIEW_RECORDING_KEY:
! 2625: if (rogue.playbackMode || serverMode) {
! 2626: return;
! 2627: }
! 2628: confirmMessages();
! 2629: if ((rogue.playerTurnNumber < 50 || confirm("End this game and view a recording?", false))
! 2630: && dialogChooseFile(path, RECORDING_SUFFIX, "View recording: ")) {
! 2631: if (fileExists(path)) {
! 2632: strcpy(rogue.nextGamePath, path);
! 2633: rogue.nextGame = NG_VIEW_RECORDING;
! 2634: rogue.gameHasEnded = true;
! 2635: } else {
! 2636: message("File not found.", false);
! 2637: }
! 2638: }
! 2639: break;
! 2640: case LOAD_SAVED_GAME_KEY:
! 2641: if (rogue.playbackMode || serverMode) {
! 2642: return;
! 2643: }
! 2644: confirmMessages();
! 2645: if ((rogue.playerTurnNumber < 50 || confirm("End this game and load a saved game?", false))
! 2646: && dialogChooseFile(path, GAME_SUFFIX, "Open saved game: ")) {
! 2647: if (fileExists(path)) {
! 2648: strcpy(rogue.nextGamePath, path);
! 2649: rogue.nextGame = NG_OPEN_GAME;
! 2650: rogue.gameHasEnded = true;
! 2651: } else {
! 2652: message("File not found.", false);
! 2653: }
! 2654: }
! 2655: break;
! 2656: case SAVE_GAME_KEY:
! 2657: if (rogue.playbackMode || serverMode) {
! 2658: return;
! 2659: }
! 2660: if (confirm("Suspend this game? (This feature is still in beta.)", false)) {
! 2661: saveGame();
! 2662: }
! 2663: break;
! 2664: case NEW_GAME_KEY:
! 2665: if (rogue.playerTurnNumber < 50 || confirm("End this game and begin a new game?", false)) {
! 2666: rogue.nextGame = NG_NEW_GAME;
! 2667: rogue.gameHasEnded = true;
! 2668: }
! 2669: break;
! 2670: case QUIT_KEY:
! 2671: if (confirm("Quit this game without saving?", false)) {
! 2672: recordKeystroke(QUIT_KEY, false, false);
! 2673: rogue.quit = true;
! 2674: gameOver("Quit", true);
! 2675: }
! 2676: break;
! 2677: case GRAPHICS_KEY:
! 2678: if (hasGraphics) {
! 2679: graphicsEnabled = setGraphicsEnabled(!graphicsEnabled);
! 2680: if (graphicsEnabled) {
! 2681: messageWithColor(KEYBOARD_LABELS ? "Enabled graphical tiles. Press 'G' again to disable." : "Enable graphical tiles.",
! 2682: &teal, false);
! 2683: } else {
! 2684: messageWithColor(KEYBOARD_LABELS ? "Disabled graphical tiles. Press 'G' again to enable." : "Disabled graphical tiles.",
! 2685: &teal, false);
! 2686: }
! 2687: }
! 2688: break;
! 2689: case SEED_KEY:
! 2690: /*DEBUG {
! 2691: cellDisplayBuffer dbuf[COLS][ROWS];
! 2692: copyDisplayBuffer(dbuf, displayBuffer);
! 2693: funkyFade(dbuf, &white, 0, 100, mapToWindowX(player.xLoc), mapToWindowY(player.yLoc), false);
! 2694: }*/
! 2695: // DEBUG displayLoops();
! 2696: // DEBUG displayChokeMap();
! 2697: DEBUG displayMachines();
! 2698: //DEBUG displayWaypoints();
! 2699: // DEBUG {displayGrid(safetyMap); displayMoreSign(); displayLevel();}
! 2700: // parseFile();
! 2701: // DEBUG spawnDungeonFeature(player.xLoc, player.yLoc, &dungeonFeatureCatalog[DF_METHANE_GAS_ARMAGEDDON], true, false);
! 2702: printSeed();
! 2703: break;
! 2704: case EASY_MODE_KEY:
! 2705: //if (shiftKey) {
! 2706: enableEasyMode();
! 2707: //}
! 2708: break;
! 2709: case PRINTSCREEN_KEY:
! 2710: if (takeScreenshot()) {
! 2711: flashTemporaryAlert(" Screenshot saved in save directory ", 2000);
! 2712: }
! 2713: break;
! 2714: default:
! 2715: break;
! 2716: }
! 2717: if (direction >= 0) { // if it was a movement command
! 2718: hideCursor();
! 2719: considerCautiousMode();
! 2720: if (controlKey || shiftKey) {
! 2721: playerRuns(direction);
! 2722: } else {
! 2723: playerMoves(direction);
! 2724: }
! 2725: refreshSideBar(-1, -1, false);
! 2726: }
! 2727:
! 2728: if (D_SAFETY_VISION) {
! 2729: displayGrid(safetyMap);
! 2730: }
! 2731: if (rogue.trueColorMode || D_SCENT_VISION) {
! 2732: displayLevel();
! 2733: }
! 2734:
! 2735: rogue.cautiousMode = false;
! 2736: }
! 2737:
! 2738: boolean getInputTextString(char *inputText,
! 2739: const char *prompt,
! 2740: short maxLength,
! 2741: const char *defaultEntry,
! 2742: const char *promptSuffix,
! 2743: short textEntryType,
! 2744: boolean useDialogBox) {
! 2745: short charNum, i, x, y;
! 2746: char keystroke, suffix[100];
! 2747: const short textEntryBounds[TEXT_INPUT_TYPES][2] = {{' ', '~'}, {' ', '~'}, {'0', '9'}};
! 2748: cellDisplayBuffer dbuf[COLS][ROWS], rbuf[COLS][ROWS];
! 2749:
! 2750: // x and y mark the origin for text entry.
! 2751: if (useDialogBox) {
! 2752: x = (COLS - max(maxLength, strLenWithoutEscapes(prompt))) / 2;
! 2753: y = ROWS / 2 - 1;
! 2754: clearDisplayBuffer(dbuf);
! 2755: rectangularShading(x - 1, y - 2, max(maxLength, strLenWithoutEscapes(prompt)) + 2,
! 2756: 4, &interfaceBoxColor, INTERFACE_OPACITY, dbuf);
! 2757: overlayDisplayBuffer(dbuf, rbuf);
! 2758: printString(prompt, x, y - 1, &white, &interfaceBoxColor, NULL);
! 2759: for (i=0; i<maxLength; i++) {
! 2760: plotCharWithColor(' ', x + i, y, &black, &black);
! 2761: }
! 2762: printString(defaultEntry, x, y, &white, &black, 0);
! 2763: } else {
! 2764: confirmMessages();
! 2765: x = mapToWindowX(strLenWithoutEscapes(prompt));
! 2766: y = MESSAGE_LINES - 1;
! 2767: message(prompt, false);
! 2768: printString(defaultEntry, x, y, &white, &black, 0);
! 2769: }
! 2770:
! 2771: maxLength = min(maxLength, COLS - x);
! 2772:
! 2773:
! 2774: strcpy(inputText, defaultEntry);
! 2775: charNum = strLenWithoutEscapes(inputText);
! 2776: for (i = charNum; i < maxLength; i++) {
! 2777: inputText[i] = ' ';
! 2778: }
! 2779:
! 2780: if (promptSuffix[0] == '\0') { // empty suffix
! 2781: strcpy(suffix, " "); // so that deleting doesn't leave a white trail
! 2782: } else {
! 2783: strcpy(suffix, promptSuffix);
! 2784: }
! 2785:
! 2786: do {
! 2787: printString(suffix, charNum + x, y, &gray, &black, 0);
! 2788: plotCharWithColor((suffix[0] ? suffix[0] : ' '), x + charNum, y, &black, &white);
! 2789: keystroke = nextKeyPress(true);
! 2790: if (keystroke == DELETE_KEY && charNum > 0) {
! 2791: printString(suffix, charNum + x - 1, y, &gray, &black, 0);
! 2792: plotCharWithColor(' ', x + charNum + strlen(suffix) - 1, y, &black, &black);
! 2793: charNum--;
! 2794: inputText[charNum] = ' ';
! 2795: } else if (keystroke >= textEntryBounds[textEntryType][0]
! 2796: && keystroke <= textEntryBounds[textEntryType][1]) { // allow only permitted input
! 2797:
! 2798: if (textEntryType == TEXT_INPUT_FILENAME
! 2799: && characterForbiddenInFilename(keystroke)) {
! 2800:
! 2801: keystroke = '-';
! 2802: }
! 2803:
! 2804: inputText[charNum] = keystroke;
! 2805: plotCharWithColor(keystroke, x + charNum, y, &white, &black);
! 2806: printString(suffix, charNum + x + 1, y, &gray, &black, 0);
! 2807: if (charNum < maxLength) {
! 2808: charNum++;
! 2809: }
! 2810: }
! 2811: #ifdef USE_CLIPBOARD
! 2812: else if (keystroke == TAB_KEY) {
! 2813: char* clipboard = getClipboard();
! 2814: for (int i=0; i<(int) min(strlen(clipboard), (unsigned long) (maxLength - charNum)); ++i) {
! 2815:
! 2816: char character = clipboard[i];
! 2817:
! 2818: if (character >= textEntryBounds[textEntryType][0]
! 2819: && character <= textEntryBounds[textEntryType][1]) { // allow only permitted input
! 2820: if (textEntryType == TEXT_INPUT_FILENAME
! 2821: && characterForbiddenInFilename(character)) {
! 2822: character = '-';
! 2823: }
! 2824: plotCharWithColor(character, x + charNum, y, &white, &black);
! 2825: if (charNum < maxLength) {
! 2826: charNum++;
! 2827: }
! 2828: }
! 2829: }
! 2830: }
! 2831: #endif
! 2832: } while (keystroke != RETURN_KEY && keystroke != ESCAPE_KEY);
! 2833:
! 2834: if (useDialogBox) {
! 2835: overlayDisplayBuffer(rbuf, NULL);
! 2836: }
! 2837:
! 2838: inputText[charNum] = '\0';
! 2839:
! 2840: if (keystroke == ESCAPE_KEY) {
! 2841: return false;
! 2842: }
! 2843: strcat(displayedMessage[0], inputText);
! 2844: strcat(displayedMessage[0], suffix);
! 2845: return true;
! 2846: }
! 2847:
! 2848: void displayCenteredAlert(char *message) {
! 2849: printString(message, (COLS - strLenWithoutEscapes(message)) / 2, ROWS / 2, &teal, &black, 0);
! 2850: }
! 2851:
! 2852: // Flashes a message on the screen starting at (x, y) lasting for the given time (in ms) and with the given colors.
! 2853: void flashMessage(char *message, short x, short y, int time, color *fColor, color *bColor) {
! 2854: boolean fastForward;
! 2855: int i, j, messageLength, percentComplete, previousPercentComplete;
! 2856: color backColors[COLS], backColor, foreColor;
! 2857: cellDisplayBuffer dbufs[COLS];
! 2858: enum displayGlyph dchar;
! 2859: short oldRNG;
! 2860: const int stepInMs = 16;
! 2861:
! 2862: if (rogue.playbackFastForward) {
! 2863: return;
! 2864: }
! 2865:
! 2866: oldRNG = rogue.RNG;
! 2867: rogue.RNG = RNG_COSMETIC;
! 2868: //assureCosmeticRNG;
! 2869:
! 2870: messageLength = strLenWithoutEscapes(message);
! 2871: fastForward = false;
! 2872:
! 2873: for (j=0; j<messageLength; j++) {
! 2874: backColors[j] = colorFromComponents(displayBuffer[j + x][y].backColorComponents);
! 2875: dbufs[j] = displayBuffer[j + x][y];
! 2876: }
! 2877:
! 2878: previousPercentComplete = -1;
! 2879: for (i=0; i < time && fastForward == false; i += stepInMs) {
! 2880: percentComplete = 100 * i / time;
! 2881: percentComplete = percentComplete * percentComplete / 100; // transition is front-loaded
! 2882: if (previousPercentComplete != percentComplete) {
! 2883: for (j=0; j<messageLength; j++) {
! 2884: if (i==0) {
! 2885: backColors[j] = colorFromComponents(displayBuffer[j + x][y].backColorComponents);
! 2886: dbufs[j] = displayBuffer[j + x][y];
! 2887: }
! 2888: backColor = backColors[j];
! 2889: applyColorAverage(&backColor, bColor, 100 - percentComplete);
! 2890: if (percentComplete < 50) {
! 2891: dchar = message[j];
! 2892: foreColor = *fColor;
! 2893: applyColorAverage(&foreColor, &backColor, percentComplete * 2);
! 2894: } else {
! 2895: dchar = dbufs[j].character;
! 2896: foreColor = colorFromComponents(dbufs[j].foreColorComponents);
! 2897: applyColorAverage(&foreColor, &backColor, (100 - percentComplete) * 2);
! 2898: }
! 2899: plotCharWithColor(dchar, j+x, y, &foreColor, &backColor);
! 2900: }
! 2901: }
! 2902: previousPercentComplete = percentComplete;
! 2903: fastForward = pauseBrogue(stepInMs);
! 2904: }
! 2905: for (j=0; j<messageLength; j++) {
! 2906: foreColor = colorFromComponents(dbufs[j].foreColorComponents);
! 2907: plotCharWithColor(dbufs[j].character, j+x, y, &foreColor, &(backColors[j]));
! 2908: }
! 2909:
! 2910: restoreRNG;
! 2911: }
! 2912:
! 2913: void flashTemporaryAlert(char *message, int time) {
! 2914: flashMessage(message, (COLS - strLenWithoutEscapes(message)) / 2, ROWS / 2, time, &teal, &black);
! 2915: }
! 2916:
! 2917: void waitForAcknowledgment() {
! 2918: rogueEvent theEvent;
! 2919:
! 2920: if (rogue.autoPlayingLevel || (rogue.playbackMode && !rogue.playbackOOS)) {
! 2921: return;
! 2922: }
! 2923:
! 2924: do {
! 2925: nextBrogueEvent(&theEvent, false, false, false);
! 2926: if (theEvent.eventType == KEYSTROKE && theEvent.param1 != ACKNOWLEDGE_KEY && theEvent.param1 != ESCAPE_KEY) {
! 2927: flashTemporaryAlert(" -- Press space or click to continue -- ", 500);
! 2928: }
! 2929: } while (!(theEvent.eventType == KEYSTROKE && (theEvent.param1 == ACKNOWLEDGE_KEY || theEvent.param1 == ESCAPE_KEY)
! 2930: || theEvent.eventType == MOUSE_UP));
! 2931: }
! 2932:
! 2933: void waitForKeystrokeOrMouseClick() {
! 2934: rogueEvent theEvent;
! 2935: do {
! 2936: nextBrogueEvent(&theEvent, false, false, false);
! 2937: } while (theEvent.eventType != KEYSTROKE && theEvent.eventType != MOUSE_UP);
! 2938: }
! 2939:
! 2940: boolean confirm(char *prompt, boolean alsoDuringPlayback) {
! 2941: short retVal;
! 2942: brogueButton buttons[2] = {{{0}}};
! 2943: cellDisplayBuffer rbuf[COLS][ROWS];
! 2944: char whiteColorEscape[20] = "";
! 2945: char yellowColorEscape[20] = "";
! 2946:
! 2947: if (rogue.autoPlayingLevel || (!alsoDuringPlayback && rogue.playbackMode)) {
! 2948: return true; // oh yes he did
! 2949: }
! 2950:
! 2951: encodeMessageColor(whiteColorEscape, 0, &white);
! 2952: encodeMessageColor(yellowColorEscape, 0, KEYBOARD_LABELS ? &yellow : &white);
! 2953:
! 2954: initializeButton(&(buttons[0]));
! 2955: sprintf(buttons[0].text, " %sY%ses ", yellowColorEscape, whiteColorEscape);
! 2956: buttons[0].hotkey[0] = 'y';
! 2957: buttons[0].hotkey[1] = 'Y';
! 2958: buttons[0].hotkey[2] = RETURN_KEY;
! 2959: buttons[0].flags |= (B_WIDE_CLICK_AREA | B_KEYPRESS_HIGHLIGHT);
! 2960:
! 2961: initializeButton(&(buttons[1]));
! 2962: sprintf(buttons[1].text, " %sN%so ", yellowColorEscape, whiteColorEscape);
! 2963: buttons[1].hotkey[0] = 'n';
! 2964: buttons[1].hotkey[1] = 'N';
! 2965: buttons[1].hotkey[2] = ACKNOWLEDGE_KEY;
! 2966: buttons[1].hotkey[3] = ESCAPE_KEY;
! 2967: buttons[1].flags |= (B_WIDE_CLICK_AREA | B_KEYPRESS_HIGHLIGHT);
! 2968:
! 2969: retVal = printTextBox(prompt, COLS/3, ROWS/3, COLS/3, &white, &interfaceBoxColor, rbuf, buttons, 2);
! 2970: overlayDisplayBuffer(rbuf, NULL);
! 2971:
! 2972: if (retVal == -1 || retVal == 1) { // If they canceled or pressed no.
! 2973: return false;
! 2974: } else {
! 2975: return true;
! 2976: }
! 2977:
! 2978: confirmMessages();
! 2979: return retVal;
! 2980: }
! 2981:
! 2982: void clearMonsterFlashes() {
! 2983:
! 2984: }
! 2985:
! 2986: void displayMonsterFlashes(boolean flashingEnabled) {
! 2987: creature *monst;
! 2988: short x[100], y[100], strength[100], count = 0;
! 2989: color *flashColor[100];
! 2990: short oldRNG;
! 2991:
! 2992: rogue.creaturesWillFlashThisTurn = false;
! 2993:
! 2994: if (rogue.autoPlayingLevel || rogue.blockCombatText) {
! 2995: return;
! 2996: }
! 2997:
! 2998: oldRNG = rogue.RNG;
! 2999: rogue.RNG = RNG_COSMETIC;
! 3000: //assureCosmeticRNG;
! 3001:
! 3002: CYCLE_MONSTERS_AND_PLAYERS(monst) {
! 3003: if (monst->bookkeepingFlags & MB_WILL_FLASH) {
! 3004: monst->bookkeepingFlags &= ~MB_WILL_FLASH;
! 3005: if (flashingEnabled && canSeeMonster(monst) && count < 100) {
! 3006: x[count] = monst->xLoc;
! 3007: y[count] = monst->yLoc;
! 3008: strength[count] = monst->flashStrength;
! 3009: flashColor[count] = &(monst->flashColor);
! 3010: count++;
! 3011: }
! 3012: }
! 3013: }
! 3014: flashForeground(x, y, flashColor, strength, count, 20);
! 3015: restoreRNG;
! 3016: }
! 3017:
! 3018: void dequeueEvent() {
! 3019: rogueEvent returnEvent;
! 3020: nextBrogueEvent(&returnEvent, false, false, true);
! 3021: }
! 3022:
! 3023: void displayMessageArchive() {
! 3024: short i, j, k, reverse, fadePercent, totalMessageCount, currentMessageCount;
! 3025: boolean fastForward;
! 3026: cellDisplayBuffer dbuf[COLS][ROWS], rbuf[COLS][ROWS];
! 3027:
! 3028: // Count the number of lines in the archive.
! 3029: for (totalMessageCount=0;
! 3030: totalMessageCount < MESSAGE_ARCHIVE_LINES && messageArchive[totalMessageCount][0];
! 3031: totalMessageCount++);
! 3032:
! 3033: if (totalMessageCount > MESSAGE_LINES) {
! 3034:
! 3035: copyDisplayBuffer(rbuf, displayBuffer);
! 3036:
! 3037: // Pull-down/pull-up animation:
! 3038: for (reverse = 0; reverse <= 1; reverse++) {
! 3039: fastForward = false;
! 3040: for (currentMessageCount = (reverse ? totalMessageCount : MESSAGE_LINES);
! 3041: (reverse ? currentMessageCount >= MESSAGE_LINES : currentMessageCount <= totalMessageCount);
! 3042: currentMessageCount += (reverse ? -1 : 1)) {
! 3043:
! 3044: clearDisplayBuffer(dbuf);
! 3045:
! 3046: // Print the message archive text to the dbuf.
! 3047: for (j=0; j < currentMessageCount && j < ROWS; j++) {
! 3048: printString(messageArchive[(messageArchivePosition - currentMessageCount + MESSAGE_ARCHIVE_LINES + j) % MESSAGE_ARCHIVE_LINES],
! 3049: mapToWindowX(0), j, &white, &black, dbuf);
! 3050: }
! 3051:
! 3052: // Set the dbuf opacity, and do a fade from bottom to top to make it clear that the bottom messages are the most recent.
! 3053: for (j=0; j < currentMessageCount && j<ROWS; j++) {
! 3054: fadePercent = 50 * (j + totalMessageCount - currentMessageCount) / totalMessageCount + 50;
! 3055: for (i=0; i<DCOLS; i++) {
! 3056: dbuf[mapToWindowX(i)][j].opacity = INTERFACE_OPACITY;
! 3057: if (dbuf[mapToWindowX(i)][j].character != ' ') {
! 3058: for (k=0; k<3; k++) {
! 3059: dbuf[mapToWindowX(i)][j].foreColorComponents[k] = dbuf[mapToWindowX(i)][j].foreColorComponents[k] * fadePercent / 100;
! 3060: }
! 3061: }
! 3062: }
! 3063: }
! 3064:
! 3065: // Display.
! 3066: overlayDisplayBuffer(rbuf, 0);
! 3067: overlayDisplayBuffer(dbuf, 0);
! 3068:
! 3069: if (!fastForward && pauseBrogue(reverse ? 1 : 2)) {
! 3070: fastForward = true;
! 3071: dequeueEvent();
! 3072: currentMessageCount = (reverse ? MESSAGE_LINES + 1 : totalMessageCount - 1); // skip to the end
! 3073: }
! 3074: }
! 3075:
! 3076: if (!reverse) {
! 3077: displayMoreSign();
! 3078: }
! 3079: }
! 3080: overlayDisplayBuffer(rbuf, 0);
! 3081: updateFlavorText();
! 3082: confirmMessages();
! 3083: updateMessageDisplay();
! 3084: }
! 3085: }
! 3086:
! 3087: // Clears the message area and prints the given message in the area.
! 3088: // It will disappear when messages are refreshed and will not be archived.
! 3089: // This is primarily used to display prompts.
! 3090: void temporaryMessage(char *msg, boolean requireAcknowledgment) {
! 3091: char message[COLS];
! 3092: short i, j;
! 3093:
! 3094: assureCosmeticRNG;
! 3095: strcpy(message, msg);
! 3096:
! 3097: for (i=0; message[i] == COLOR_ESCAPE; i += 4) {
! 3098: upperCase(&(message[i]));
! 3099: }
! 3100:
! 3101: refreshSideBar(-1, -1, false);
! 3102:
! 3103: for (i=0; i<MESSAGE_LINES; i++) {
! 3104: for (j=0; j<DCOLS; j++) {
! 3105: plotCharWithColor(' ', mapToWindowX(j), i, &black, &black);
! 3106: }
! 3107: }
! 3108: printString(message, mapToWindowX(0), mapToWindowY(-1), &white, &black, 0);
! 3109: if (requireAcknowledgment) {
! 3110: waitForAcknowledgment();
! 3111: updateMessageDisplay();
! 3112: }
! 3113: restoreRNG;
! 3114: }
! 3115:
! 3116: void messageWithColor(char *msg, color *theColor, boolean requireAcknowledgment) {
! 3117: char buf[COLS*2] = "";
! 3118: short i;
! 3119:
! 3120: i=0;
! 3121: i = encodeMessageColor(buf, i, theColor);
! 3122: strcpy(&(buf[i]), msg);
! 3123: message(buf, requireAcknowledgment);
! 3124: }
! 3125:
! 3126: void flavorMessage(char *msg) {
! 3127: short i;
! 3128: char text[COLS*20];
! 3129:
! 3130: for (i=0; i < COLS*2 && msg[i] != '\0'; i++) {
! 3131: text[i] = msg[i];
! 3132: }
! 3133: text[i] = '\0';
! 3134:
! 3135: for(i=0; text[i] == COLOR_ESCAPE; i+=4);
! 3136: upperCase(&(text[i]));
! 3137:
! 3138: printString(text, mapToWindowX(0), ROWS - 2, &flavorTextColor, &black, 0);
! 3139: for (i = strLenWithoutEscapes(text); i < DCOLS; i++) {
! 3140: plotCharWithColor(' ', mapToWindowX(i), ROWS - 2, &black, &black);
! 3141: }
! 3142: }
! 3143:
! 3144: void messageWithoutCaps(char *msg, boolean requireAcknowledgment) {
! 3145: short i;
! 3146: if (!msg[0]) {
! 3147: return;
! 3148: }
! 3149:
! 3150: // need to confirm the oldest message? (Disabled!)
! 3151: /*if (!messageConfirmed[MESSAGE_LINES - 1]) {
! 3152: //refreshSideBar(-1, -1, false);
! 3153: displayMoreSign();
! 3154: for (i=0; i<MESSAGE_LINES; i++) {
! 3155: messageConfirmed[i] = true;
! 3156: }
! 3157: }*/
! 3158:
! 3159: for (i = MESSAGE_LINES - 1; i >= 1; i--) {
! 3160: messageConfirmed[i] = messageConfirmed[i-1];
! 3161: strcpy(displayedMessage[i], displayedMessage[i-1]);
! 3162: }
! 3163: messageConfirmed[0] = false;
! 3164: strcpy(displayedMessage[0], msg);
! 3165:
! 3166: // Add the message to the archive.
! 3167: strcpy(messageArchive[messageArchivePosition], msg);
! 3168: messageArchivePosition = (messageArchivePosition + 1) % MESSAGE_ARCHIVE_LINES;
! 3169:
! 3170: // display the message:
! 3171: updateMessageDisplay();
! 3172:
! 3173: if (requireAcknowledgment || rogue.cautiousMode) {
! 3174: displayMoreSign();
! 3175: confirmMessages();
! 3176: rogue.cautiousMode = false;
! 3177: }
! 3178:
! 3179: if (rogue.playbackMode) {
! 3180: rogue.playbackDelayThisTurn += rogue.playbackDelayPerTurn * 5;
! 3181: }
! 3182: }
! 3183:
! 3184:
! 3185: void message(const char *msg, boolean requireAcknowledgment) {
! 3186: char text[COLS*20], *msgPtr;
! 3187: short i, lines;
! 3188:
! 3189: assureCosmeticRNG;
! 3190:
! 3191: rogue.disturbed = true;
! 3192: if (requireAcknowledgment) {
! 3193: refreshSideBar(-1, -1, false);
! 3194: }
! 3195: displayCombatText();
! 3196:
! 3197: lines = wrapText(text, msg, DCOLS);
! 3198: msgPtr = &(text[0]);
! 3199:
! 3200: // Implement the American quotation mark/period/comma ordering rule.
! 3201: for (i=0; text[i] != '\0' && text[i+1] != '\0'; i++) {
! 3202: if (text[i] == COLOR_ESCAPE) {
! 3203: i += 4;
! 3204: } else if (text[i] == '"'
! 3205: && (text[i+1] == '.' || text[i+1] == ',')) {
! 3206:
! 3207: text[i] = text[i+1];
! 3208: text[i+1] = '"';
! 3209: }
! 3210: }
! 3211:
! 3212: for(i=0; text[i] == COLOR_ESCAPE; i+=4);
! 3213: upperCase(&(text[i]));
! 3214:
! 3215: if (lines > 1) {
! 3216: for (i=0; text[i] != '\0'; i++) {
! 3217: if (text[i] == '\n') {
! 3218: text[i] = '\0';
! 3219:
! 3220: messageWithoutCaps(msgPtr, false);
! 3221: msgPtr = &(text[i+1]);
! 3222: }
! 3223: }
! 3224: }
! 3225:
! 3226: messageWithoutCaps(msgPtr, requireAcknowledgment);
! 3227: restoreRNG;
! 3228: }
! 3229:
! 3230: // Only used for the "you die..." message, to enable posthumous inventory viewing.
! 3231: void displayMoreSignWithoutWaitingForAcknowledgment() {
! 3232: if (strLenWithoutEscapes(displayedMessage[0]) < DCOLS - 8 || messageConfirmed[0]) {
! 3233: printString("--MORE--", COLS - 8, MESSAGE_LINES-1, &black, &white, 0);
! 3234: } else {
! 3235: printString("--MORE--", COLS - 8, MESSAGE_LINES, &black, &white, 0);
! 3236: }
! 3237: }
! 3238:
! 3239: void displayMoreSign() {
! 3240: short i;
! 3241:
! 3242: if (rogue.autoPlayingLevel) {
! 3243: return;
! 3244: }
! 3245:
! 3246: if (strLenWithoutEscapes(displayedMessage[0]) < DCOLS - 8 || messageConfirmed[0]) {
! 3247: printString("--MORE--", COLS - 8, MESSAGE_LINES-1, &black, &white, 0);
! 3248: waitForAcknowledgment();
! 3249: printString(" ", COLS - 8, MESSAGE_LINES-1, &black, &black, 0);
! 3250: } else {
! 3251: printString("--MORE--", COLS - 8, MESSAGE_LINES, &black, &white, 0);
! 3252: waitForAcknowledgment();
! 3253: for (i=1; i<=8; i++) {
! 3254: refreshDungeonCell(DCOLS - i, 0);
! 3255: }
! 3256: }
! 3257: }
! 3258:
! 3259: // Inserts a four-character color escape sequence into a string at the insertion point.
! 3260: // Does NOT check string lengths, so it could theoretically write over the null terminator.
! 3261: // Returns the new insertion point.
! 3262: short encodeMessageColor(char *msg, short i, const color *theColor) {
! 3263: boolean needTerminator = false;
! 3264: color col = *theColor;
! 3265:
! 3266: assureCosmeticRNG;
! 3267:
! 3268: bakeColor(&col);
! 3269:
! 3270: col.red = clamp(col.red, 0, 100);
! 3271: col.green = clamp(col.green, 0, 100);
! 3272: col.blue = clamp(col.blue, 0, 100);
! 3273:
! 3274: needTerminator = !msg[i] || !msg[i + 1] || !msg[i + 2] || !msg[i + 3];
! 3275:
! 3276: msg[i++] = COLOR_ESCAPE;
! 3277: msg[i++] = (char) (COLOR_VALUE_INTERCEPT + col.red);
! 3278: msg[i++] = (char) (COLOR_VALUE_INTERCEPT + col.green);
! 3279: msg[i++] = (char) (COLOR_VALUE_INTERCEPT + col.blue);
! 3280:
! 3281: if (needTerminator) {
! 3282: msg[i] = '\0';
! 3283: }
! 3284:
! 3285: restoreRNG;
! 3286:
! 3287: return i;
! 3288: }
! 3289:
! 3290: // Call this when the i'th character of msg is COLOR_ESCAPE.
! 3291: // It will return the encoded color, and will advance i past the color escape sequence.
! 3292: short decodeMessageColor(const char *msg, short i, color *returnColor) {
! 3293:
! 3294: if (msg[i] != COLOR_ESCAPE) {
! 3295: printf("\nAsked to decode a color escape that didn't exist!");
! 3296: *returnColor = white;
! 3297: } else {
! 3298: i++;
! 3299: *returnColor = black;
! 3300: returnColor->red = (short) (msg[i++] - COLOR_VALUE_INTERCEPT);
! 3301: returnColor->green = (short) (msg[i++] - COLOR_VALUE_INTERCEPT);
! 3302: returnColor->blue = (short) (msg[i++] - COLOR_VALUE_INTERCEPT);
! 3303:
! 3304: returnColor->red = clamp(returnColor->red, 0, 100);
! 3305: returnColor->green = clamp(returnColor->green, 0, 100);
! 3306: returnColor->blue = clamp(returnColor->blue, 0, 100);
! 3307: }
! 3308: return i;
! 3309: }
! 3310:
! 3311: // Returns a color for combat text based on the identity of the victim.
! 3312: color *messageColorFromVictim(creature *monst) {
! 3313: if (monst == &player) {
! 3314: return &badMessageColor;
! 3315: } else if (player.status[STATUS_HALLUCINATING] && !rogue.playbackOmniscience) {
! 3316: return &white;
! 3317: } else if (monst->creatureState == MONSTER_ALLY) {
! 3318: return &badMessageColor;
! 3319: } else if (monstersAreEnemies(&player, monst)) {
! 3320: return &goodMessageColor;
! 3321: } else {
! 3322: return &white;
! 3323: }
! 3324: }
! 3325:
! 3326: void updateMessageDisplay() {
! 3327: short i, j, m;
! 3328: color messageColor;
! 3329:
! 3330: for (i=0; i<MESSAGE_LINES; i++) {
! 3331: messageColor = white;
! 3332:
! 3333: if (messageConfirmed[i]) {
! 3334: applyColorAverage(&messageColor, &black, 50);
! 3335: applyColorAverage(&messageColor, &black, 75 * i / MESSAGE_LINES);
! 3336: }
! 3337:
! 3338: for (j = m = 0; displayedMessage[i][m] && j < DCOLS; j++, m++) {
! 3339:
! 3340: while (displayedMessage[i][m] == COLOR_ESCAPE) {
! 3341: m = decodeMessageColor(displayedMessage[i], m, &messageColor); // pulls the message color out and advances m
! 3342: if (messageConfirmed[i]) {
! 3343: applyColorAverage(&messageColor, &black, 50);
! 3344: applyColorAverage(&messageColor, &black, 75 * i / MESSAGE_LINES);
! 3345: }
! 3346: }
! 3347:
! 3348: plotCharWithColor(displayedMessage[i][m], mapToWindowX(j), MESSAGE_LINES - i - 1,
! 3349: &messageColor,
! 3350: &black);
! 3351: }
! 3352: for (; j < DCOLS; j++) {
! 3353: plotCharWithColor(' ', mapToWindowX(j), MESSAGE_LINES - i - 1, &black, &black);
! 3354: }
! 3355: }
! 3356: }
! 3357:
! 3358: // Does NOT clear the message archive.
! 3359: void deleteMessages() {
! 3360: short i;
! 3361: for (i=0; i<MESSAGE_LINES; i++) {
! 3362: displayedMessage[i][0] = '\0';
! 3363: }
! 3364: confirmMessages();
! 3365: }
! 3366:
! 3367: void confirmMessages() {
! 3368: short i;
! 3369: for (i=0; i<MESSAGE_LINES; i++) {
! 3370: messageConfirmed[i] = true;
! 3371: }
! 3372: updateMessageDisplay();
! 3373: }
! 3374:
! 3375: void stripShiftFromMovementKeystroke(signed long *keystroke) {
! 3376: const unsigned short newKey = *keystroke - ('A' - 'a');
! 3377: if (newKey == LEFT_KEY
! 3378: || newKey == RIGHT_KEY
! 3379: || newKey == DOWN_KEY
! 3380: || newKey == UP_KEY
! 3381: || newKey == UPLEFT_KEY
! 3382: || newKey == UPRIGHT_KEY
! 3383: || newKey == DOWNLEFT_KEY
! 3384: || newKey == DOWNRIGHT_KEY) {
! 3385: *keystroke -= 'A' - 'a';
! 3386: }
! 3387: }
! 3388:
! 3389: void upperCase(char *theChar) {
! 3390: if (*theChar >= 'a' && *theChar <= 'z') {
! 3391: (*theChar) += ('A' - 'a');
! 3392: }
! 3393: }
! 3394:
! 3395: enum entityDisplayTypes {
! 3396: EDT_NOTHING = 0,
! 3397: EDT_CREATURE,
! 3398: EDT_ITEM,
! 3399: EDT_TERRAIN,
! 3400: };
! 3401:
! 3402: // Refreshes the sidebar.
! 3403: // Progresses from the closest visible monster to the farthest.
! 3404: // If a monster, item or terrain is focused, then display the sidebar with that monster/item highlighted,
! 3405: // in the order it would normally appear. If it would normally not fit on the sidebar at all,
! 3406: // then list it first.
! 3407: // Also update rogue.sidebarLocationList[ROWS][2] list of locations so that each row of
! 3408: // the screen is mapped to the corresponding entity, if any.
! 3409: // FocusedEntityMustGoFirst should usually be false when called externally. This is because
! 3410: // we won't know if it will fit on the screen in normal order until we try.
! 3411: // So if we try and fail, this function will call itself again, but with this set to true.
! 3412: void refreshSideBar(short focusX, short focusY, boolean focusedEntityMustGoFirst) {
! 3413: short printY, oldPrintY, shortestDistance, i, j, k, px, py, x = 0, y = 0, displayEntityCount, indirectVision;
! 3414: creature *monst = NULL, *closestMonst = NULL;
! 3415: item *theItem, *closestItem = NULL;
! 3416: char buf[COLS];
! 3417: void *entityList[ROWS] = {0}, *focusEntity = NULL;
! 3418: enum entityDisplayTypes entityType[ROWS] = {0}, focusEntityType = EDT_NOTHING;
! 3419: short terrainLocationMap[ROWS][2];
! 3420: boolean gotFocusedEntityOnScreen = (focusX >= 0 ? false : true);
! 3421: char addedEntity[DCOLS][DROWS];
! 3422: short oldRNG;
! 3423:
! 3424: if (rogue.gameHasEnded || rogue.playbackFastForward) {
! 3425: return;
! 3426: }
! 3427:
! 3428: oldRNG = rogue.RNG;
! 3429: rogue.RNG = RNG_COSMETIC;
! 3430: //assureCosmeticRNG;
! 3431:
! 3432: if (focusX < 0) {
! 3433: focusedEntityMustGoFirst = false; // just in case!
! 3434: } else {
! 3435: if (pmap[focusX][focusY].flags & (HAS_MONSTER | HAS_PLAYER)) {
! 3436: monst = monsterAtLoc(focusX, focusY);
! 3437: if (canSeeMonster(monst) || rogue.playbackOmniscience) {
! 3438: focusEntity = monst;
! 3439: focusEntityType = EDT_CREATURE;
! 3440: }
! 3441: }
! 3442: if (!focusEntity && (pmap[focusX][focusY].flags & HAS_ITEM)) {
! 3443: theItem = itemAtLoc(focusX, focusY);
! 3444: if (playerCanSeeOrSense(focusX, focusY)) {
! 3445: focusEntity = theItem;
! 3446: focusEntityType = EDT_ITEM;
! 3447: }
! 3448: }
! 3449: if (!focusEntity
! 3450: && cellHasTMFlag(focusX, focusY, TM_LIST_IN_SIDEBAR)
! 3451: && playerCanSeeOrSense(focusX, focusY)) {
! 3452:
! 3453: focusEntity = tileCatalog[pmap[focusX][focusY].layers[layerWithTMFlag(focusX, focusY, TM_LIST_IN_SIDEBAR)]].description;
! 3454: focusEntityType = EDT_TERRAIN;
! 3455: }
! 3456: }
! 3457:
! 3458: printY = 0;
! 3459:
! 3460: px = player.xLoc;
! 3461: py = player.yLoc;
! 3462:
! 3463: zeroOutGrid(addedEntity);
! 3464:
! 3465: // Header information for playback mode.
! 3466: if (rogue.playbackMode) {
! 3467: printString(" -- PLAYBACK -- ", 0, printY++, &white, &black, 0);
! 3468: if (rogue.howManyTurns > 0) {
! 3469: sprintf(buf, "Turn %li/%li", rogue.playerTurnNumber, rogue.howManyTurns);
! 3470: printProgressBar(0, printY++, buf, rogue.playerTurnNumber, rogue.howManyTurns, &darkPurple, false);
! 3471: }
! 3472: if (rogue.playbackOOS) {
! 3473: printString(" [OUT OF SYNC] ", 0, printY++, &badMessageColor, &black, 0);
! 3474: } else if (rogue.playbackPaused) {
! 3475: printString(" [PAUSED] ", 0, printY++, &gray, &black, 0);
! 3476: }
! 3477: printString(" ", 0, printY++, &white, &black, 0);
! 3478: }
! 3479:
! 3480: // Now list the monsters that we'll be displaying in the order of their proximity to player (listing the focused first if required).
! 3481:
! 3482: // Initialization.
! 3483: displayEntityCount = 0;
! 3484: for (i=0; i<ROWS*2; i++) {
! 3485: rogue.sidebarLocationList[i][0] = -1;
! 3486: rogue.sidebarLocationList[i][1] = -1;
! 3487: }
! 3488:
! 3489: // Player always goes first.
! 3490: entityList[displayEntityCount] = &player;
! 3491: entityType[displayEntityCount] = EDT_CREATURE;
! 3492: displayEntityCount++;
! 3493: addedEntity[player.xLoc][player.yLoc] = true;
! 3494:
! 3495: // Focused entity, if it must go first.
! 3496: if (focusedEntityMustGoFirst && !addedEntity[focusX][focusY]) {
! 3497: addedEntity[focusX][focusY] = true;
! 3498: entityList[displayEntityCount] = focusEntity;
! 3499: entityType[displayEntityCount] = focusEntityType;
! 3500: terrainLocationMap[displayEntityCount][0] = focusX;
! 3501: terrainLocationMap[displayEntityCount][1] = focusY;
! 3502: displayEntityCount++;
! 3503: }
! 3504:
! 3505: for (indirectVision = 0; indirectVision < 2; indirectVision++) {
! 3506: // Non-focused monsters.
! 3507: do {
! 3508: shortestDistance = 10000;
! 3509: for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
! 3510: if ((canDirectlySeeMonster(monst) || (indirectVision && (canSeeMonster(monst) || rogue.playbackOmniscience)))
! 3511: && !addedEntity[monst->xLoc][monst->yLoc]
! 3512: && !(monst->info.flags & MONST_NOT_LISTED_IN_SIDEBAR)
! 3513: && (px - monst->xLoc) * (px - monst->xLoc) + (py - monst->yLoc) * (py - monst->yLoc) < shortestDistance) {
! 3514:
! 3515: shortestDistance = (px - monst->xLoc) * (px - monst->xLoc) + (py - monst->yLoc) * (py - monst->yLoc);
! 3516: closestMonst = monst;
! 3517: }
! 3518: }
! 3519: if (shortestDistance < 10000) {
! 3520: addedEntity[closestMonst->xLoc][closestMonst->yLoc] = true;
! 3521: entityList[displayEntityCount] = closestMonst;
! 3522: entityType[displayEntityCount] = EDT_CREATURE;
! 3523: displayEntityCount++;
! 3524: }
! 3525: } while (shortestDistance < 10000 && displayEntityCount * 2 < ROWS); // Because each entity takes at least 2 rows in the sidebar.
! 3526:
! 3527: // Non-focused items.
! 3528: do {
! 3529: shortestDistance = 10000;
! 3530: for (theItem = floorItems->nextItem; theItem != NULL; theItem = theItem->nextItem) {
! 3531: if ((playerCanDirectlySee(theItem->xLoc, theItem->yLoc) || (indirectVision && (playerCanSeeOrSense(theItem->xLoc, theItem->yLoc) || rogue.playbackOmniscience)))
! 3532: && !addedEntity[theItem->xLoc][theItem->yLoc]
! 3533: && (px - theItem->xLoc) * (px - theItem->xLoc) + (py - theItem->yLoc) * (py - theItem->yLoc) < shortestDistance) {
! 3534:
! 3535: shortestDistance = (px - theItem->xLoc) * (px - theItem->xLoc) + (py - theItem->yLoc) * (py - theItem->yLoc);
! 3536: closestItem = theItem;
! 3537: }
! 3538: }
! 3539: if (shortestDistance < 10000) {
! 3540: addedEntity[closestItem->xLoc][closestItem->yLoc] = true;
! 3541: entityList[displayEntityCount] = closestItem;
! 3542: entityType[displayEntityCount] = EDT_ITEM;
! 3543: displayEntityCount++;
! 3544: }
! 3545: } while (shortestDistance < 10000 && displayEntityCount * 2 < ROWS); // Because each entity takes at least 2 rows in the sidebar.
! 3546:
! 3547: // Non-focused terrain.
! 3548:
! 3549: // count up the number of candidate locations
! 3550: for (k=0; k<max(DROWS, DCOLS); k++) {
! 3551: for (i = px-k; i <= px+k; i++) {
! 3552: // we are scanning concentric squares. The first and last columns
! 3553: // need to be stepped through, but others can be jumped over.
! 3554: short step = (i == px-k || i == px+k) ? 1 : 2*k;
! 3555: for (j = py-k; j <= py+k; j += step) {
! 3556: if (displayEntityCount >= ROWS - 1) goto no_space_for_more_entities;
! 3557: if (coordinatesAreInMap(i, j)
! 3558: && !addedEntity[i][j]
! 3559: && cellHasTMFlag(i, j, TM_LIST_IN_SIDEBAR)
! 3560: && (playerCanDirectlySee(i, j) || (indirectVision && (playerCanSeeOrSense(i, j) || rogue.playbackOmniscience)))) {
! 3561:
! 3562: addedEntity[i][j] = true;
! 3563: entityList[displayEntityCount] = tileCatalog[pmap[i][j].layers[layerWithTMFlag(i, j, TM_LIST_IN_SIDEBAR)]].description;
! 3564: entityType[displayEntityCount] = EDT_TERRAIN;
! 3565: terrainLocationMap[displayEntityCount][0] = i;
! 3566: terrainLocationMap[displayEntityCount][1] = j;
! 3567: displayEntityCount++;
! 3568: }
! 3569: }
! 3570: }
! 3571: }
! 3572: no_space_for_more_entities:;
! 3573: }
! 3574:
! 3575: // Entities are now listed. Start printing.
! 3576:
! 3577: for (i=0; i<displayEntityCount && printY < ROWS - 1; i++) { // Bottom line is reserved for the depth.
! 3578: oldPrintY = printY;
! 3579: if (entityType[i] == EDT_CREATURE) {
! 3580: x = ((creature *) entityList[i])->xLoc;
! 3581: y = ((creature *) entityList[i])->yLoc;
! 3582: printY = printMonsterInfo((creature *) entityList[i],
! 3583: printY,
! 3584: (focusEntity && (x != focusX || y != focusY)),
! 3585: (x == focusX && y == focusY));
! 3586:
! 3587: } else if (entityType[i] == EDT_ITEM) {
! 3588: x = ((item *) entityList[i])->xLoc;
! 3589: y = ((item *) entityList[i])->yLoc;
! 3590: printY = printItemInfo((item *) entityList[i],
! 3591: printY,
! 3592: (focusEntity && (x != focusX || y != focusY)),
! 3593: (x == focusX && y == focusY));
! 3594: } else if (entityType[i] == EDT_TERRAIN) {
! 3595: x = terrainLocationMap[i][0];
! 3596: y = terrainLocationMap[i][1];
! 3597: printY = printTerrainInfo(x, y,
! 3598: printY,
! 3599: ((const char *) entityList[i]),
! 3600: (focusEntity && (x != focusX || y != focusY)),
! 3601: (x == focusX && y == focusY));
! 3602: }
! 3603: if (focusEntity && (x == focusX && y == focusY) && printY < ROWS) {
! 3604: gotFocusedEntityOnScreen = true;
! 3605: }
! 3606: for (j=oldPrintY; j<printY; j++) {
! 3607: rogue.sidebarLocationList[j][0] = x;
! 3608: rogue.sidebarLocationList[j][1] = y;
! 3609: }
! 3610: }
! 3611:
! 3612: if (gotFocusedEntityOnScreen) {
! 3613: // Wrap things up.
! 3614: for (i=printY; i< ROWS - 1; i++) {
! 3615: printString(" ", 0, i, &white, &black, 0);
! 3616: }
! 3617: sprintf(buf, " -- Depth: %i --%s ", rogue.depthLevel, (rogue.depthLevel < 10 ? " " : ""));
! 3618: printString(buf, 0, ROWS - 1, &white, &black, 0);
! 3619: } else if (!focusedEntityMustGoFirst) {
! 3620: // Failed to get the focusMonst printed on the screen. Try again, this time with the focus first.
! 3621: refreshSideBar(focusX, focusY, true);
! 3622: }
! 3623:
! 3624: restoreRNG;
! 3625: }
! 3626:
! 3627: void printString(const char *theString, short x, short y, color *foreColor, color *backColor, cellDisplayBuffer dbuf[COLS][ROWS]) {
! 3628: color fColor;
! 3629: short i;
! 3630:
! 3631: fColor = *foreColor;
! 3632:
! 3633: for (i=0; theString[i] != '\0' && x < COLS; i++, x++) {
! 3634: while (theString[i] == COLOR_ESCAPE) {
! 3635: i = decodeMessageColor(theString, i, &fColor);
! 3636: if (!theString[i]) {
! 3637: return;
! 3638: }
! 3639: }
! 3640:
! 3641: if (dbuf) {
! 3642: plotCharToBuffer(theString[i], x, y, &fColor, backColor, dbuf);
! 3643: } else {
! 3644: plotCharWithColor(theString[i], x, y, &fColor, backColor);
! 3645: }
! 3646: }
! 3647: }
! 3648:
! 3649: // Inserts line breaks into really long words. Optionally adds a hyphen, but doesn't do anything
! 3650: // clever regarding hyphen placement. Plays nicely with color escapes.
! 3651: void breakUpLongWordsIn(char *sourceText, short width, boolean useHyphens) {
! 3652: char buf[COLS * ROWS * 2] = "";
! 3653: short i, m, nextChar, wordWidth;
! 3654: //const short maxLength = useHyphens ? width - 1 : width;
! 3655:
! 3656: // i iterates over characters in sourceText; m keeps track of the length of buf.
! 3657: wordWidth = 0;
! 3658: for (i=0, m=0; sourceText[i] != 0;) {
! 3659: if (sourceText[i] == COLOR_ESCAPE) {
! 3660: strncpy(&(buf[m]), &(sourceText[i]), 4);
! 3661: i += 4;
! 3662: m += 4;
! 3663: } else if (sourceText[i] == ' ' || sourceText[i] == '\n') {
! 3664: wordWidth = 0;
! 3665: buf[m++] = sourceText[i++];
! 3666: } else {
! 3667: if (!useHyphens && wordWidth >= width) {
! 3668: buf[m++] = '\n';
! 3669: wordWidth = 0;
! 3670: } else if (useHyphens && wordWidth >= width - 1) {
! 3671: nextChar = i+1;
! 3672: while (sourceText[nextChar] == COLOR_ESCAPE) {
! 3673: nextChar += 4;
! 3674: }
! 3675: if (sourceText[nextChar] && sourceText[nextChar] != ' ' && sourceText[nextChar] != '\n') {
! 3676: buf[m++] = '-';
! 3677: buf[m++] = '\n';
! 3678: wordWidth = 0;
! 3679: }
! 3680: }
! 3681: buf[m++] = sourceText[i++];
! 3682: wordWidth++;
! 3683: }
! 3684: }
! 3685: buf[m] = '\0';
! 3686: strcpy(sourceText, buf);
! 3687: }
! 3688:
! 3689: // Returns the number of lines, including the newlines already in the text.
! 3690: // Puts the output in "to" only if we receive a "to" -- can make it null and just get a line count.
! 3691: short wrapText(char *to, const char *sourceText, short width) {
! 3692: short i, w, textLength, lineCount;
! 3693: char printString[COLS * ROWS * 2];
! 3694: short spaceLeftOnLine, wordWidth;
! 3695:
! 3696: strcpy(printString, sourceText); // a copy we can write on
! 3697: breakUpLongWordsIn(printString, width, true); // break up any words that are wider than the width.
! 3698:
! 3699: textLength = strlen(printString); // do NOT discount escape sequences
! 3700: lineCount = 1;
! 3701:
! 3702: // Now go through and replace spaces with newlines as needed.
! 3703:
! 3704: // Fast foward until i points to the first character that is not a color escape.
! 3705: for (i=0; printString[i] == COLOR_ESCAPE; i+= 4);
! 3706: spaceLeftOnLine = width;
! 3707:
! 3708: while (i < textLength) {
! 3709: // wordWidth counts the word width of the next word without color escapes.
! 3710: // w indicates the position of the space or newline or null terminator that terminates the word.
! 3711: wordWidth = 0;
! 3712: for (w = i + 1; w < textLength && printString[w] != ' ' && printString[w] != '\n';) {
! 3713: if (printString[w] == COLOR_ESCAPE) {
! 3714: w += 4;
! 3715: } else {
! 3716: w++;
! 3717: wordWidth++;
! 3718: }
! 3719: }
! 3720:
! 3721: if (1 + wordWidth > spaceLeftOnLine || printString[i] == '\n') {
! 3722: printString[i] = '\n';
! 3723: lineCount++;
! 3724: spaceLeftOnLine = width - wordWidth; // line width minus the width of the word we just wrapped
! 3725: //printf("\n\n%s", printString);
! 3726: } else {
! 3727: spaceLeftOnLine -= 1 + wordWidth;
! 3728: }
! 3729: i = w; // Advance to the terminator that follows the word.
! 3730: }
! 3731: if (to) {
! 3732: strcpy(to, printString);
! 3733: }
! 3734: return lineCount;
! 3735: }
! 3736:
! 3737: // returns the y-coordinate of the last line
! 3738: short printStringWithWrapping(char *theString, short x, short y, short width, color *foreColor,
! 3739: color*backColor, cellDisplayBuffer dbuf[COLS][ROWS]) {
! 3740: color fColor;
! 3741: char printString[COLS * ROWS * 2];
! 3742: short i, px, py;
! 3743:
! 3744: wrapText(printString, theString, width); // inserts newlines as necessary
! 3745:
! 3746: // display the string
! 3747: px = x; //px and py are the print insertion coordinates; x and y remain the top-left of the text box
! 3748: py = y;
! 3749: fColor = *foreColor;
! 3750:
! 3751: for (i=0; printString[i] != '\0'; i++) {
! 3752: if (printString[i] == '\n') {
! 3753: px = x; // back to the leftmost column
! 3754: if (py < ROWS - 1) { // don't advance below the bottom of the screen
! 3755: py++; // next line
! 3756: } else {
! 3757: break; // If we've run out of room, stop.
! 3758: }
! 3759: continue;
! 3760: } else if (printString[i] == COLOR_ESCAPE) {
! 3761: i = decodeMessageColor(printString, i, &fColor) - 1;
! 3762: continue;
! 3763: }
! 3764:
! 3765: if (dbuf) {
! 3766: if (coordinatesAreInWindow(px, py)) {
! 3767: plotCharToBuffer(printString[i], px, py, &fColor, backColor, dbuf);
! 3768: }
! 3769: } else {
! 3770: if (coordinatesAreInWindow(px, py)) {
! 3771: plotCharWithColor(printString[i], px, py, &fColor, backColor);
! 3772: }
! 3773: }
! 3774:
! 3775: px++;
! 3776: }
! 3777: return py;
! 3778: }
! 3779:
! 3780: char nextKeyPress(boolean textInput) {
! 3781: rogueEvent theEvent;
! 3782: do {
! 3783: nextBrogueEvent(&theEvent, textInput, false, false);
! 3784: } while (theEvent.eventType != KEYSTROKE);
! 3785: return theEvent.param1;
! 3786: }
! 3787:
! 3788: #define BROGUE_HELP_LINE_COUNT 33
! 3789:
! 3790: void printHelpScreen() {
! 3791: short i, j;
! 3792: cellDisplayBuffer dbuf[COLS][ROWS], rbuf[COLS][ROWS];
! 3793: char helpText[BROGUE_HELP_LINE_COUNT][DCOLS*3] = {
! 3794: "",
! 3795: "",
! 3796: " -- Commands --",
! 3797: "",
! 3798: " mouse ****move cursor (including to examine monsters and terrain)",
! 3799: " click ****travel",
! 3800: " control-click ****advance one space",
! 3801: " <return> ****enable keyboard cursor control",
! 3802: " <space/esc> ****disable keyboard cursor control",
! 3803: "hjklyubn, arrow keys, or numpad ****move or attack (control or shift to run)",
! 3804: "",
! 3805: " a/e/r/t/d/c/R ****apply/equip/remove/throw/drop/call/relabel an item",
! 3806: " T ****re-throw last item at last monster",
! 3807: "i, right-click ****view inventory",
! 3808: " D ****list discovered items",
! 3809: "",
! 3810: " z ****rest once",
! 3811: " Z ****rest for 100 turns or until something happens",
! 3812: " s ****search for secrets (control-s: long search)",
! 3813: " <, > ****travel to stairs",
! 3814: " x ****auto-explore (control-x: fast forward)",
! 3815: " A ****autopilot (control-A: fast forward)",
! 3816: " M ****display old messages",
! 3817: " G ****toggle graphical tiles (when available)",
! 3818: "",
! 3819: " S ****suspend game and quit",
! 3820: " Q ****quit to title screen",
! 3821: "",
! 3822: " \\ ****disable/enable color effects",
! 3823: " ] ****display/hide stealth range",
! 3824: " <space/esc> ****clear message or cancel command",
! 3825: "",
! 3826: " -- press space or click to continue --"
! 3827: };
! 3828:
! 3829: // Replace the "****"s with color escapes.
! 3830: for (i=0; i<BROGUE_HELP_LINE_COUNT; i++) {
! 3831: for (j=0; helpText[i][j]; j++) {
! 3832: if (helpText[i][j] == '*') {
! 3833: j = encodeMessageColor(helpText[i], j, &white);
! 3834: }
! 3835: }
! 3836: }
! 3837:
! 3838: clearDisplayBuffer(dbuf);
! 3839:
! 3840: // Print the text to the dbuf.
! 3841: for (i=0; i<BROGUE_HELP_LINE_COUNT && i < ROWS; i++) {
! 3842: printString(helpText[i], mapToWindowX(1), i, &itemMessageColor, &black, dbuf);
! 3843: }
! 3844:
! 3845: // Set the dbuf opacity.
! 3846: for (i=0; i<DCOLS; i++) {
! 3847: for (j=0; j<ROWS; j++) {
! 3848: //plotCharWithColor(' ', mapToWindowX(i), j, &black, &black);
! 3849: dbuf[mapToWindowX(i)][j].opacity = INTERFACE_OPACITY;
! 3850: }
! 3851: }
! 3852:
! 3853: // Display.
! 3854: overlayDisplayBuffer(dbuf, rbuf);
! 3855: waitForAcknowledgment();
! 3856: overlayDisplayBuffer(rbuf, 0);
! 3857: updateFlavorText();
! 3858: updateMessageDisplay();
! 3859: }
! 3860:
! 3861: void printDiscoveries(short category, short count, unsigned short itemCharacter, short x, short y, cellDisplayBuffer dbuf[COLS][ROWS]) {
! 3862: color *theColor, goodColor, badColor;
! 3863: char buf[COLS], buf2[COLS];
! 3864: short i, magic, totalFrequency;
! 3865: itemTable *theTable = tableForItemCategory(category, NULL);
! 3866:
! 3867: goodColor = goodMessageColor;
! 3868: applyColorAverage(&goodColor, &black, 50);
! 3869: badColor = badMessageColor;
! 3870: applyColorAverage(&badColor, &black, 50);
! 3871:
! 3872: totalFrequency = 0;
! 3873: for (i = 0; i < count; i++) {
! 3874: if (!theTable[i].identified) {
! 3875: totalFrequency += theTable[i].frequency;
! 3876: }
! 3877: }
! 3878:
! 3879: for (i = 0; i < count; i++) {
! 3880: if (theTable[i].identified) {
! 3881: theColor = &white;
! 3882: plotCharToBuffer(itemCharacter, x, y + i, &itemColor, &black, dbuf);
! 3883: } else {
! 3884: theColor = &darkGray;
! 3885: magic = magicCharDiscoverySuffix(category, i);
! 3886: if (magic == 1) {
! 3887: plotCharToBuffer(G_GOOD_MAGIC, x, y + i, &goodColor, &black, dbuf);
! 3888: } else if (magic == -1) {
! 3889: plotCharToBuffer(G_BAD_MAGIC, x, y + i, &badColor, &black, dbuf);
! 3890: }
! 3891: }
! 3892: strcpy(buf, theTable[i].name);
! 3893:
! 3894: if (!theTable[i].identified
! 3895: && theTable[i].frequency > 0
! 3896: && totalFrequency > 0) {
! 3897:
! 3898: sprintf(buf2, " (%i%%)", theTable[i].frequency * 100 / totalFrequency);
! 3899: strcat(buf, buf2);
! 3900: }
! 3901:
! 3902: upperCase(buf);
! 3903: strcat(buf, " ");
! 3904: printString(buf, x + 2, y + i, theColor, &black, dbuf);
! 3905: }
! 3906: }
! 3907:
! 3908: void printDiscoveriesScreen() {
! 3909: short i, j, y;
! 3910: cellDisplayBuffer dbuf[COLS][ROWS], rbuf[COLS][ROWS];
! 3911:
! 3912: clearDisplayBuffer(dbuf);
! 3913:
! 3914: printString("-- SCROLLS --", mapToWindowX(2), y = mapToWindowY(1), &flavorTextColor, &black, dbuf);
! 3915: printDiscoveries(SCROLL, NUMBER_SCROLL_KINDS, G_SCROLL, mapToWindowX(3), ++y, dbuf);
! 3916:
! 3917: printString("-- RINGS --", mapToWindowX(2), y += NUMBER_SCROLL_KINDS + 1, &flavorTextColor, &black, dbuf);
! 3918: printDiscoveries(RING, NUMBER_RING_KINDS, G_RING, mapToWindowX(3), ++y, dbuf);
! 3919:
! 3920: printString("-- POTIONS --", mapToWindowX(29), y = mapToWindowY(1), &flavorTextColor, &black, dbuf);
! 3921: printDiscoveries(POTION, NUMBER_POTION_KINDS, G_POTION, mapToWindowX(30), ++y, dbuf);
! 3922:
! 3923: printString("-- STAFFS --", mapToWindowX(53), y = mapToWindowY(1), &flavorTextColor, &black, dbuf);
! 3924: printDiscoveries(STAFF, NUMBER_STAFF_KINDS, G_STAFF, mapToWindowX(54), ++y, dbuf);
! 3925:
! 3926: printString("-- WANDS --", mapToWindowX(53), y += NUMBER_STAFF_KINDS + 1, &flavorTextColor, &black, dbuf);
! 3927: printDiscoveries(WAND, NUMBER_WAND_KINDS, G_WAND, mapToWindowX(54), ++y, dbuf);
! 3928:
! 3929: printString(KEYBOARD_LABELS ? "-- press any key to continue --" : "-- touch anywhere to continue --",
! 3930: mapToWindowX(20), mapToWindowY(DROWS-2), &itemMessageColor, &black, dbuf);
! 3931:
! 3932: for (i=0; i<COLS; i++) {
! 3933: for (j=0; j<ROWS; j++) {
! 3934: dbuf[i][j].opacity = (i < STAT_BAR_WIDTH ? 0 : INTERFACE_OPACITY);
! 3935: }
! 3936: }
! 3937: overlayDisplayBuffer(dbuf, rbuf);
! 3938:
! 3939: waitForKeystrokeOrMouseClick();
! 3940:
! 3941: overlayDisplayBuffer(rbuf, NULL);
! 3942: }
! 3943:
! 3944: // Creates buttons for the discoveries screen in the buttons pointer; returns the number of buttons created.
! 3945: //short createDiscoveriesButtons(short category, short count, unsigned short itemCharacter, short x, short y, brogueButton *buttons) {
! 3946: // color goodColor, badColor;
! 3947: // char whiteColorEscape[20] = "", darkGrayColorEscape[20] = "", yellowColorEscape[20] = "", goodColorEscape[20] = "", badColorEscape[20] = "";
! 3948: // short i, magic, symbolCount;
! 3949: // itemTable *theTable = tableForItemCategory(category, NULL);
! 3950: // char buf[COLS] = "";
! 3951: //
! 3952: // goodColor = goodMessageColor;
! 3953: // applyColorAverage(&goodColor, &black, 50);
! 3954: // encodeMessageColor(goodColorEscape, 0, &goodColor);
! 3955: // badColor = badMessageColor;
! 3956: // applyColorAverage(&badColor, &black, 50);
! 3957: // encodeMessageColor(badColorEscape, 0, &badColor);
! 3958: // encodeMessageColor(whiteColorEscape, 0, &white);
! 3959: // encodeMessageColor(darkGrayColorEscape, 0, &darkGray);
! 3960: // encodeMessageColor(yellowColorEscape, 0, &itemColor);
! 3961: //
! 3962: // for (i = 0; i < count; i++) {
! 3963: // initializeButton(&(buttons[i]));
! 3964: // buttons[i].flags = (B_DRAW | B_HOVER_ENABLED | B_ENABLED); // Clear other flags.
! 3965: // buttons[i].buttonColor = black;
! 3966: // buttons[i].opacity = 100;
! 3967: // buttons[i].x = x;
! 3968: // buttons[i].y = y + i;
! 3969: // symbolCount = 0;
! 3970: // if (theTable[i].identified) {
! 3971: // strcat(buttons[i].text, yellowColorEscape);
! 3972: // buttons[i].symbol[symbolCount++] = itemCharacter;
! 3973: // strcat(buttons[i].text, "*");
! 3974: // strcat(buttons[i].text, whiteColorEscape);
! 3975: // strcat(buttons[i].text, " ");
! 3976: // } else {
! 3977: // strcat(buttons[i].text, " ");
! 3978: // strcat(buttons[i].text, darkGrayColorEscape);
! 3979: // }
! 3980: // strcpy(buf, theTable[i].name);
! 3981: // upperCase(buf);
! 3982: // strcat(buttons[i].text, buf);
! 3983: // strcat(buttons[i].text, " ");
! 3984: // strcat(buttons[i].text, darkGrayColorEscape);
! 3985: // magic = magicCharDiscoverySuffix(category, i);
! 3986: // strcat(buttons[i].text, "(");
! 3987: // if (magic != -1) {
! 3988: // strcat(buttons[i].text, goodColorEscape);
! 3989: // strcat(buttons[i].text, "*");
! 3990: // buttons[i].symbol[symbolCount++] = G_GOOD_MAGIC;
! 3991: // }
! 3992: // if (magic != 1) {
! 3993: // strcat(buttons[i].text, badColorEscape);
! 3994: // strcat(buttons[i].text, "*");
! 3995: // buttons[i].symbol[symbolCount++] = BAD_MAGIC_CHAR;
! 3996: // }
! 3997: // strcat(buttons[i].text, darkGrayColorEscape);
! 3998: // strcat(buttons[i].text, ")");
! 3999: // }
! 4000: // return i;
! 4001: //}
! 4002: //
! 4003: //void printDiscoveriesScreen() {
! 4004: // short i, j, y, buttonCount;
! 4005: // cellDisplayBuffer dbuf[COLS][ROWS], rbuf[COLS][ROWS];
! 4006: // brogueButton buttons[NUMBER_SCROLL_KINDS + NUMBER_WAND_KINDS + NUMBER_POTION_KINDS + NUMBER_STAFF_KINDS + NUMBER_RING_KINDS] = {{{0}}};
! 4007: // rogueEvent theEvent;
! 4008: //
! 4009: // clearDisplayBuffer(dbuf);
! 4010: // buttonCount = 0;
! 4011: //
! 4012: // printString("-- SCROLLS --", mapToWindowX(3), y = mapToWindowY(1), &flavorTextColor, &black, dbuf);
! 4013: // buttonCount += createDiscoveriesButtons(SCROLL, NUMBER_SCROLL_KINDS, SCROLL_CHAR, mapToWindowX(3), ++y, &(buttons[buttonCount]));
! 4014: //
! 4015: // printString("-- WANDS --", mapToWindowX(3), y += NUMBER_SCROLL_KINDS + 1, &flavorTextColor, &black, dbuf);
! 4016: // buttonCount += createDiscoveriesButtons(WAND, NUMBER_WAND_KINDS, WAND_CHAR, mapToWindowX(3), ++y, &(buttons[buttonCount]));
! 4017: //
! 4018: // printString("-- POTIONS --", mapToWindowX(29), y = mapToWindowY(1), &flavorTextColor, &black, dbuf);
! 4019: // buttonCount += createDiscoveriesButtons(POTION, NUMBER_POTION_KINDS, POTION_CHAR, mapToWindowX(29), ++y, &(buttons[buttonCount]));
! 4020: //
! 4021: // printString("-- STAFFS --", mapToWindowX(54), y = mapToWindowY(1), &flavorTextColor, &black, dbuf);
! 4022: // buttonCount += createDiscoveriesButtons(STAFF, NUMBER_STAFF_KINDS, STAFF_CHAR, mapToWindowX(54), ++y, &(buttons[buttonCount]));
! 4023: //
! 4024: // printString("-- RINGS --", mapToWindowX(54), y += NUMBER_STAFF_KINDS + 1, &flavorTextColor, &black, dbuf);
! 4025: // buttonCount += createDiscoveriesButtons(RING, NUMBER_RING_KINDS, RING_CHAR, mapToWindowX(54), ++y, &(buttons[buttonCount]));
! 4026: //
! 4027: // for (i=0; i<COLS; i++) {
! 4028: // for (j=0; j<ROWS; j++) {
! 4029: // dbuf[i][j].opacity = (i < STAT_BAR_WIDTH ? 0 : INTERFACE_OPACITY);
! 4030: // }
! 4031: // }
! 4032: // overlayDisplayBuffer(dbuf, rbuf);
! 4033: // y = buttonInputLoop(buttons,
! 4034: // buttonCount,
! 4035: // mapToWindowX(3),
! 4036: // mapToWindowY(1),
! 4037: // DCOLS - 6,
! 4038: // DROWS - 2,
! 4039: // &theEvent);
! 4040: // overlayDisplayBuffer(rbuf, NULL);
! 4041: //}
! 4042:
! 4043: void printHighScores(boolean hiliteMostRecent) {
! 4044: short i, hiliteLineNum, maxLength = 0, leftOffset;
! 4045: rogueHighScoresEntry list[HIGH_SCORES_COUNT] = {{0}};
! 4046: char buf[DCOLS*3];
! 4047: color scoreColor;
! 4048:
! 4049: hiliteLineNum = getHighScoresList(list);
! 4050:
! 4051: if (!hiliteMostRecent) {
! 4052: hiliteLineNum = -1;
! 4053: }
! 4054:
! 4055: blackOutScreen();
! 4056:
! 4057: for (i = 0; i < HIGH_SCORES_COUNT && list[i].score > 0; i++) {
! 4058: if (strLenWithoutEscapes(list[i].description) > maxLength) {
! 4059: maxLength = strLenWithoutEscapes(list[i].description);
! 4060: }
! 4061: }
! 4062:
! 4063: leftOffset = min(COLS - maxLength - 23 - 1, COLS/5);
! 4064:
! 4065: scoreColor = black;
! 4066: applyColorAverage(&scoreColor, &itemMessageColor, 100);
! 4067: printString("-- HIGH SCORES --", (COLS - 17 + 1) / 2, 0, &scoreColor, &black, 0);
! 4068:
! 4069: for (i = 0; i < HIGH_SCORES_COUNT && list[i].score > 0; i++) {
! 4070: scoreColor = black;
! 4071: if (i == hiliteLineNum) {
! 4072: applyColorAverage(&scoreColor, &itemMessageColor, 100);
! 4073: } else {
! 4074: applyColorAverage(&scoreColor, &white, 100);
! 4075: applyColorAverage(&scoreColor, &black, (i * 50 / 24));
! 4076: }
! 4077:
! 4078: // rank
! 4079: sprintf(buf, "%s%i)", (i + 1 < 10 ? " " : ""), i + 1);
! 4080: printString(buf, leftOffset, i + 2, &scoreColor, &black, 0);
! 4081:
! 4082: // score
! 4083: sprintf(buf, "%li", list[i].score);
! 4084: printString(buf, leftOffset + 5, i + 2, &scoreColor, &black, 0);
! 4085:
! 4086: // date
! 4087: printString(list[i].date, leftOffset + 12, i + 2, &scoreColor, &black, 0);
! 4088:
! 4089: // description
! 4090: printString(list[i].description, leftOffset + 23, i + 2, &scoreColor, &black, 0);
! 4091: }
! 4092:
! 4093: scoreColor = black;
! 4094: applyColorAverage(&scoreColor, &goodMessageColor, 100);
! 4095:
! 4096: printString(KEYBOARD_LABELS ? "Press space to continue." : "Touch anywhere to continue.",
! 4097: (COLS - strLenWithoutEscapes(KEYBOARD_LABELS ? "Press space to continue." : "Touch anywhere to continue.")) / 2,
! 4098: ROWS - 1, &scoreColor, &black, 0);
! 4099:
! 4100: commitDraws();
! 4101: waitForAcknowledgment();
! 4102: }
! 4103:
! 4104: void displayGrid(short **map) {
! 4105: short i, j, score, topRange, bottomRange;
! 4106: color tempColor, foreColor, backColor;
! 4107: enum displayGlyph dchar;
! 4108:
! 4109: topRange = -30000;
! 4110: bottomRange = 30000;
! 4111: tempColor = black;
! 4112:
! 4113: if (map == safetyMap && !rogue.updatedSafetyMapThisTurn) {
! 4114: updateSafetyMap();
! 4115: }
! 4116:
! 4117: for (i=0; i<DCOLS; i++) {
! 4118: for (j=0; j<DROWS; j++) {
! 4119: if (cellHasTerrainFlag(i, j, T_WAYPOINT_BLOCKER) || (map[i][j] == map[0][0]) || (i == player.xLoc && j == player.yLoc)) {
! 4120: continue;
! 4121: }
! 4122: if (map[i][j] > topRange) {
! 4123: topRange = map[i][j];
! 4124: //if (topRange == 0) {
! 4125: //printf("\ntop is zero at %i,%i", i, j);
! 4126: //}
! 4127: }
! 4128: if (map[i][j] < bottomRange) {
! 4129: bottomRange = map[i][j];
! 4130: }
! 4131: }
! 4132: }
! 4133:
! 4134: for (i=0; i<DCOLS; i++) {
! 4135: for (j=0; j<DROWS; j++) {
! 4136: if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY | T_LAVA_INSTA_DEATH)
! 4137: || (map[i][j] == map[0][0])
! 4138: || (i == player.xLoc && j == player.yLoc)) {
! 4139: continue;
! 4140: }
! 4141: score = 300 - (map[i][j] - bottomRange) * 300 / max(1, (topRange - bottomRange));
! 4142: tempColor.blue = max(min(score, 100), 0);
! 4143: score -= 100;
! 4144: tempColor.red = max(min(score, 100), 0);
! 4145: score -= 100;
! 4146: tempColor.green = max(min(score, 100), 0);
! 4147: getCellAppearance(i, j, &dchar, &foreColor, &backColor);
! 4148: plotCharWithColor(dchar, mapToWindowX(i), mapToWindowY(j), &foreColor, &tempColor);
! 4149: //colorBlendCell(i, j, &tempColor, 100);//hiliteCell(i, j, &tempColor, 100, false);
! 4150: }
! 4151: }
! 4152: //printf("\ntop: %i; bottom: %i", topRange, bottomRange);
! 4153: }
! 4154:
! 4155: void printSeed() {
! 4156: char buf[COLS];
! 4157: sprintf(buf, "Dungeon seed #%lu; turn #%lu; version %s", rogue.seed, rogue.playerTurnNumber, BROGUE_VERSION_STRING);
! 4158: message(buf, false);
! 4159: }
! 4160:
! 4161: void printProgressBar(short x, short y, const char barLabel[COLS], long amtFilled, long amtMax, color *fillColor, boolean dim) {
! 4162: char barText[] = " "; // string length is 20
! 4163: short i, labelOffset;
! 4164: color currentFillColor, textColor, progressBarColor, darkenedBarColor;
! 4165:
! 4166: if (y >= ROWS - 1) { // don't write over the depth number
! 4167: return;
! 4168: }
! 4169:
! 4170: if (amtFilled > amtMax) {
! 4171: amtFilled = amtMax;
! 4172: }
! 4173:
! 4174: if (amtMax <= 0) {
! 4175: amtMax = 1;
! 4176: }
! 4177:
! 4178: progressBarColor = *fillColor;
! 4179: if (!(y % 2)) {
! 4180: applyColorAverage(&progressBarColor, &black, 25);
! 4181: }
! 4182:
! 4183: if (dim) {
! 4184: applyColorAverage(&progressBarColor, &black, 50);
! 4185: }
! 4186: darkenedBarColor = progressBarColor;
! 4187: applyColorAverage(&darkenedBarColor, &black, 75);
! 4188:
! 4189: labelOffset = (20 - strlen(barLabel)) / 2;
! 4190: for (i = 0; i < (short) strlen(barLabel); i++) {
! 4191: barText[i + labelOffset] = barLabel[i];
! 4192: }
! 4193:
! 4194: amtFilled = clamp(amtFilled, 0, amtMax);
! 4195:
! 4196: if (amtMax < 10000000) {
! 4197: amtFilled *= 100;
! 4198: amtMax *= 100;
! 4199: }
! 4200:
! 4201: for (i=0; i<20; i++) {
! 4202: currentFillColor = (i <= (20 * amtFilled / amtMax) ? progressBarColor : darkenedBarColor);
! 4203: if (i == 20 * amtFilled / amtMax) {
! 4204: applyColorAverage(¤tFillColor, &black, 75 - 75 * (amtFilled % (amtMax / 20)) / (amtMax / 20));
! 4205: }
! 4206: textColor = (dim ? gray : white);
! 4207: applyColorAverage(&textColor, ¤tFillColor, (dim ? 50 : 33));
! 4208: plotCharWithColor(barText[i], x + i, y, &textColor, ¤tFillColor);
! 4209: }
! 4210: }
! 4211:
! 4212: // Very low-level. Changes displayBuffer directly.
! 4213: void highlightScreenCell(short x, short y, color *highlightColor, short strength) {
! 4214: color tempColor;
! 4215:
! 4216: tempColor = colorFromComponents(displayBuffer[x][y].foreColorComponents);
! 4217: applyColorAugment(&tempColor, highlightColor, strength);
! 4218: storeColorComponents(displayBuffer[x][y].foreColorComponents, &tempColor);
! 4219:
! 4220: tempColor = colorFromComponents(displayBuffer[x][y].backColorComponents);
! 4221: applyColorAugment(&tempColor, highlightColor, strength);
! 4222: storeColorComponents(displayBuffer[x][y].backColorComponents, &tempColor);
! 4223:
! 4224: displayBuffer[x][y].needsUpdate = true;
! 4225: }
! 4226:
! 4227: short estimatedArmorValue() {
! 4228: short retVal;
! 4229:
! 4230: retVal = ((armorTable[rogue.armor->kind].range.upperBound + armorTable[rogue.armor->kind].range.lowerBound) / 2) / 10;
! 4231: retVal += strengthModifier(rogue.armor) / FP_FACTOR;
! 4232: retVal -= player.status[STATUS_DONNING];
! 4233:
! 4234: return max(0, retVal);
! 4235: }
! 4236:
! 4237: short creatureHealthChangePercent(creature *monst) {
! 4238: if (monst->previousHealthPoints <= 0) {
! 4239: return 0;
! 4240: }
! 4241: // ignore overhealing from tranference
! 4242: return 100 * (monst->currentHP - min(monst->previousHealthPoints, monst->info.maxHP)) / monst->info.maxHP;
! 4243: }
! 4244:
! 4245: // returns the y-coordinate after the last line printed
! 4246: short printMonsterInfo(creature *monst, short y, boolean dim, boolean highlight) {
! 4247: char buf[COLS * 2], buf2[COLS * 2], monstName[COLS], tempColorEscape[5], grayColorEscape[5];
! 4248: enum displayGlyph monstChar;
! 4249: color monstForeColor, monstBackColor, healthBarColor, tempColor;
! 4250: short initialY, i, j, highlightStrength, displayedArmor, percent;
! 4251: boolean inPath;
! 4252: short oldRNG;
! 4253:
! 4254: const char hallucinationStrings[16][COLS] = {
! 4255: " (Dancing) ",
! 4256: " (Singing) ",
! 4257: " (Pontificating) ",
! 4258: " (Skipping) ",
! 4259: " (Spinning) ",
! 4260: " (Crying) ",
! 4261: " (Laughing) ",
! 4262: " (Humming) ",
! 4263: " (Whistling) ",
! 4264: " (Quivering) ",
! 4265: " (Muttering) ",
! 4266: " (Gibbering) ",
! 4267: " (Giggling) ",
! 4268: " (Moaning) ",
! 4269: " (Shrieking) ",
! 4270: " (Caterwauling) ",
! 4271: };
! 4272: const char statusStrings[NUMBER_OF_STATUS_EFFECTS][COLS] = {
! 4273: "Searching",
! 4274: "Donning Armor",
! 4275: "Weakened: -",
! 4276: "Telepathic",
! 4277: "Hallucinating",
! 4278: "Levitating",
! 4279: "Slowed",
! 4280: "Hasted",
! 4281: "Confused",
! 4282: "Burning",
! 4283: "Paralyzed",
! 4284: "Poisoned",
! 4285: "Stuck",
! 4286: "Nauseous",
! 4287: "Discordant",
! 4288: "Immune to Fire",
! 4289: "", // STATUS_EXPLOSION_IMMUNITY,
! 4290: "", // STATUS_NUTRITION,
! 4291: "", // STATUS_ENTERS_LEVEL_IN,
! 4292: "Frightened",
! 4293: "Entranced",
! 4294: "Darkened",
! 4295: "Lifespan",
! 4296: "Shielded",
! 4297: "Invisible",
! 4298: };
! 4299:
! 4300: if (y >= ROWS - 1) {
! 4301: return ROWS - 1;
! 4302: }
! 4303:
! 4304: initialY = y;
! 4305:
! 4306: oldRNG = rogue.RNG;
! 4307: rogue.RNG = RNG_COSMETIC;
! 4308: //assureCosmeticRNG;
! 4309:
! 4310: if (y < ROWS - 1) {
! 4311: printString(" ", 0, y, &white, &black, 0); // Start with a blank line
! 4312:
! 4313: // Unhighlight if it's highlighted as part of the path.
! 4314: inPath = (pmap[monst->xLoc][monst->yLoc].flags & IS_IN_PATH) ? true : false;
! 4315: pmap[monst->xLoc][monst->yLoc].flags &= ~IS_IN_PATH;
! 4316: getCellAppearance(monst->xLoc, monst->yLoc, &monstChar, &monstForeColor, &monstBackColor);
! 4317: applyColorBounds(&monstForeColor, 0, 100);
! 4318: applyColorBounds(&monstBackColor, 0, 100);
! 4319: if (inPath) {
! 4320: pmap[monst->xLoc][monst->yLoc].flags |= IS_IN_PATH;
! 4321: }
! 4322:
! 4323: if (dim) {
! 4324: applyColorAverage(&monstForeColor, &black, 50);
! 4325: applyColorAverage(&monstBackColor, &black, 50);
! 4326: } else if (highlight) {
! 4327: applyColorAugment(&monstForeColor, &black, 100);
! 4328: applyColorAugment(&monstBackColor, &black, 100);
! 4329: }
! 4330: plotCharWithColor(monstChar, 0, y, &monstForeColor, &monstBackColor);
! 4331: if(monst->carriedItem) {
! 4332: plotCharWithColor(monst->carriedItem->displayChar, 1, y, &itemColor, &black);
! 4333: }
! 4334: monsterName(monstName, monst, false);
! 4335: upperCase(monstName);
! 4336:
! 4337: if (monst == &player) {
! 4338: if (player.status[STATUS_INVISIBLE]) {
! 4339: strcat(monstName, " xxxx");
! 4340: encodeMessageColor(monstName, strlen(monstName) - 4, &monstForeColor);
! 4341: strcat(monstName, "(invisible)");
! 4342: } else if (playerInDarkness()) {
! 4343: strcat(monstName, " xxxx");
! 4344: //encodeMessageColor(monstName, strlen(monstName) - 4, &playerInDarknessColor);
! 4345: encodeMessageColor(monstName, strlen(monstName) - 4, &monstForeColor);
! 4346: strcat(monstName, "(dark)");
! 4347: } else if (!(pmap[player.xLoc][player.yLoc].flags & IS_IN_SHADOW)) {
! 4348: strcat(monstName, " xxxx");
! 4349: //encodeMessageColor(monstName, strlen(monstName) - 4, &playerInLightColor);
! 4350: encodeMessageColor(monstName, strlen(monstName) - 4, &monstForeColor);
! 4351: strcat(monstName, "(lit)");
! 4352: }
! 4353: }
! 4354:
! 4355: sprintf(buf, ": %s", monstName);
! 4356: printString(buf, monst->carriedItem?2:1, y++, (dim ? &gray : &white), &black, 0);
! 4357: }
! 4358:
! 4359: // mutation, if any
! 4360: if (y < ROWS - 1
! 4361: && monst->mutationIndex >= 0
! 4362: && (!player.status[STATUS_HALLUCINATING] || rogue.playbackOmniscience)) {
! 4363:
! 4364: strcpy(buf, " ");
! 4365: sprintf(buf2, "xxxx(%s)", mutationCatalog[monst->mutationIndex].title);
! 4366: tempColor = *mutationCatalog[monst->mutationIndex].textColor;
! 4367: if (dim) {
! 4368: applyColorAverage(&tempColor, &black, 50);
! 4369: }
! 4370: encodeMessageColor(buf2, 0, &tempColor);
! 4371: strcpy(buf + ((strLenWithoutEscapes(buf) - strLenWithoutEscapes(buf2)) / 2), buf2);
! 4372: for (i = strlen(buf); i < 20 + 4; i++) {
! 4373: buf[i] = ' ';
! 4374: }
! 4375: buf[24] = '\0';
! 4376: printString(buf, 0, y++, (dim ? &gray : &white), &black, 0);
! 4377: }
! 4378:
! 4379: // hit points
! 4380: if (monst->info.maxHP > 1
! 4381: && !(monst->info.flags & MONST_INVULNERABLE)) {
! 4382:
! 4383: if (monst == &player) {
! 4384: healthBarColor = redBar;
! 4385: applyColorAverage(&healthBarColor, &blueBar, min(100, 100 * player.currentHP / player.info.maxHP));
! 4386: } else {
! 4387: healthBarColor = blueBar;
! 4388: }
! 4389: percent = creatureHealthChangePercent(monst);
! 4390: if (monst->currentHP <= 0) {
! 4391: strcpy(buf, "Dead");
! 4392: } else if (percent != 0) {
! 4393: strcpy(buf, " Health ");
! 4394: sprintf(buf2, "(%s%i%%)", percent > 0 ? "+" : "", percent);
! 4395: strcpy(&(buf[20 - strlen(buf2)]), buf2);
! 4396: } else {
! 4397: strcpy(buf, "Health");
! 4398: }
! 4399: printProgressBar(0, y++, buf, monst->currentHP, monst->info.maxHP, &healthBarColor, dim);
! 4400: }
! 4401:
! 4402: if (monst == &player) {
! 4403: // nutrition
! 4404: if (player.status[STATUS_NUTRITION] > HUNGER_THRESHOLD) {
! 4405: printProgressBar(0, y++, "Nutrition", player.status[STATUS_NUTRITION], STOMACH_SIZE, &blueBar, dim);
! 4406: } else if (player.status[STATUS_NUTRITION] > WEAK_THRESHOLD) {
! 4407: printProgressBar(0, y++, "Nutrition (Hungry)", player.status[STATUS_NUTRITION], STOMACH_SIZE, &blueBar, dim);
! 4408: } else if (player.status[STATUS_NUTRITION] > FAINT_THRESHOLD) {
! 4409: printProgressBar(0, y++, "Nutrition (Weak)", player.status[STATUS_NUTRITION], STOMACH_SIZE, &blueBar, dim);
! 4410: } else if (player.status[STATUS_NUTRITION] > 0) {
! 4411: printProgressBar(0, y++, "Nutrition (Faint)", player.status[STATUS_NUTRITION], STOMACH_SIZE, &blueBar, dim);
! 4412: } else if (y < ROWS - 1) {
! 4413: printString(" STARVING ", 0, y++, &badMessageColor, &black, NULL);
! 4414: }
! 4415: }
! 4416:
! 4417: if (!player.status[STATUS_HALLUCINATING] || rogue.playbackOmniscience || monst == &player) {
! 4418:
! 4419: for (i=0; i<NUMBER_OF_STATUS_EFFECTS; i++) {
! 4420: if (i == STATUS_WEAKENED && monst->status[i] > 0) {
! 4421: sprintf(buf, "%s%i", statusStrings[STATUS_WEAKENED], monst->weaknessAmount);
! 4422: printProgressBar(0, y++, buf, monst->status[i], monst->maxStatus[i], &redBar, dim);
! 4423: } else if (i == STATUS_LEVITATING && monst->status[i] > 0) {
! 4424: printProgressBar(0, y++, (monst == &player ? "Levitating" : "Flying"), monst->status[i], monst->maxStatus[i], &redBar, dim);
! 4425: } else if (i == STATUS_POISONED
! 4426: && monst->status[i] > 0) {
! 4427:
! 4428:
! 4429: if (monst->status[i] * monst->poisonAmount >= monst->currentHP) {
! 4430: strcpy(buf, "Fatal Poison");
! 4431: } else {
! 4432: strcpy(buf, "Poisoned");
! 4433: }
! 4434: if (monst->poisonAmount == 1) {
! 4435: printProgressBar(0, y++, buf, monst->status[i], monst->maxStatus[i], &redBar, dim);
! 4436: } else {
! 4437: sprintf(buf2, "%s (x%i)",
! 4438: buf,
! 4439: monst->poisonAmount);
! 4440: printProgressBar(0, y++, buf2, monst->status[i], monst->maxStatus[i], &redBar, dim);
! 4441: }
! 4442: } else if (statusStrings[i][0] && monst->status[i] > 0) {
! 4443: printProgressBar(0, y++, statusStrings[i], monst->status[i], monst->maxStatus[i], &redBar, dim);
! 4444: }
! 4445: }
! 4446: if (monst->targetCorpseLoc[0] == monst->xLoc && monst->targetCorpseLoc[1] == monst->yLoc) {
! 4447: printProgressBar(0, y++, monsterText[monst->info.monsterID].absorbStatus, monst->corpseAbsorptionCounter, 20, &redBar, dim);
! 4448: }
! 4449: }
! 4450:
! 4451: if (monst != &player
! 4452: && (!(monst->info.flags & MONST_INANIMATE)
! 4453: || monst->creatureState == MONSTER_ALLY)) {
! 4454:
! 4455: if (y < ROWS - 1) {
! 4456: if (player.status[STATUS_HALLUCINATING] && !rogue.playbackOmniscience && y < ROWS - 1) {
! 4457: printString(hallucinationStrings[rand_range(0, 9)], 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4458: } else if (monst->bookkeepingFlags & MB_CAPTIVE && y < ROWS - 1) {
! 4459: printString(" (Captive) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4460: } else if ((monst->info.flags & MONST_RESTRICTED_TO_LIQUID)
! 4461: && !cellHasTMFlag(monst->xLoc, monst->yLoc, TM_ALLOWS_SUBMERGING)) {
! 4462: printString(" (Helpless) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4463: } else if (monst->creatureState == MONSTER_SLEEPING && y < ROWS - 1) {
! 4464: printString(" (Sleeping) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4465: } else if ((monst->creatureState == MONSTER_ALLY) && y < ROWS - 1) {
! 4466: printString(" (Ally) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4467: } else if (monst->creatureState == MONSTER_FLEEING && y < ROWS - 1) {
! 4468: printString(" (Fleeing) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4469: } else if ((monst->creatureState == MONSTER_WANDERING) && y < ROWS - 1) {
! 4470: if ((monst->bookkeepingFlags & MB_FOLLOWER) && monst->leader && (monst->leader->info.flags & MONST_IMMOBILE)) {
! 4471: // follower of an immobile leader -- i.e. a totem
! 4472: printString(" (Worshiping) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4473: } else if ((monst->bookkeepingFlags & MB_FOLLOWER) && monst->leader && (monst->leader->bookkeepingFlags & MB_CAPTIVE)) {
! 4474: // actually a captor/torturer
! 4475: printString(" (Guarding) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4476: } else {
! 4477: printString(" (Wandering) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4478: }
! 4479: } else if (monst->ticksUntilTurn > max(0, player.ticksUntilTurn) + player.movementSpeed) {
! 4480: printString(" (Off balance) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4481: } else if ((monst->creatureState == MONSTER_TRACKING_SCENT) && y < ROWS - 1) {
! 4482: printString(" (Hunting) ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4483: }
! 4484: }
! 4485: } else if (monst == &player) {
! 4486: if (y < ROWS - 1) {
! 4487: tempColorEscape[0] = '\0';
! 4488: grayColorEscape[0] = '\0';
! 4489: if (player.status[STATUS_WEAKENED]) {
! 4490: tempColor = red;
! 4491: if (dim) {
! 4492: applyColorAverage(&tempColor, &black, 50);
! 4493: }
! 4494: encodeMessageColor(tempColorEscape, 0, &tempColor);
! 4495: encodeMessageColor(grayColorEscape, 0, (dim ? &darkGray : &gray));
! 4496: }
! 4497:
! 4498: displayedArmor = displayedArmorValue();
! 4499:
! 4500: if (!rogue.armor || rogue.armor->flags & ITEM_IDENTIFIED || rogue.playbackOmniscience) {
! 4501:
! 4502: sprintf(buf, "Str: %s%i%s Armor: %i",
! 4503: tempColorEscape,
! 4504: rogue.strength - player.weaknessAmount,
! 4505: grayColorEscape,
! 4506: displayedArmor);
! 4507: } else {
! 4508: sprintf(buf, "Str: %s%i%s Armor: %i?",
! 4509: tempColorEscape,
! 4510: rogue.strength - player.weaknessAmount,
! 4511: grayColorEscape,
! 4512: estimatedArmorValue());
! 4513: }
! 4514: //buf[20] = '\0';
! 4515: printString(" ", 0, y, &white, &black, 0);
! 4516: printString(buf, (20 - strLenWithoutEscapes(buf)) / 2, y++, (dim ? &darkGray : &gray), &black, 0);
! 4517: }
! 4518: if (y < ROWS - 1 && rogue.gold) {
! 4519: sprintf(buf, "Gold: %li", rogue.gold);
! 4520: buf[20] = '\0';
! 4521: printString(" ", 0, y, &white, &black, 0);
! 4522: printString(buf, (20 - strLenWithoutEscapes(buf)) / 2, y++, (dim ? &darkGray : &gray), &black, 0);
! 4523: }
! 4524: if (y < ROWS - 1) {
! 4525: tempColorEscape[0] = '\0';
! 4526: grayColorEscape[0] = '\0';
! 4527: tempColor = playerInShadowColor;
! 4528: percent = (rogue.aggroRange - 2) * 100 / 28;
! 4529: applyColorAverage(&tempColor, &black, percent);
! 4530: applyColorAugment(&tempColor, &playerInLightColor, percent);
! 4531: if (dim) {
! 4532: applyColorAverage(&tempColor, &black, 50);
! 4533: }
! 4534: encodeMessageColor(tempColorEscape, 0, &tempColor);
! 4535: encodeMessageColor(grayColorEscape, 0, (dim ? &darkGray : &gray));
! 4536: sprintf(buf, "%sStealth range: %i%s",
! 4537: tempColorEscape,
! 4538: rogue.aggroRange,
! 4539: grayColorEscape);
! 4540: printString(" ", 0, y, &white, &black, 0);
! 4541: printString(buf, 1, y++, (dim ? &darkGray : &gray), &black, 0);
! 4542: }
! 4543: }
! 4544:
! 4545: if (y < ROWS - 1) {
! 4546: printString(" ", 0, y++, (dim ? &darkGray : &gray), &black, 0);
! 4547: }
! 4548:
! 4549: if (highlight) {
! 4550: for (i=0; i<20; i++) {
! 4551: highlightStrength = smoothHiliteGradient(i, 20-1) / 10;
! 4552: for (j=initialY; j < (y == ROWS - 1 ? y : min(y - 1, ROWS - 1)); j++) {
! 4553: highlightScreenCell(i, j, &white, highlightStrength);
! 4554: }
! 4555: }
! 4556: }
! 4557:
! 4558: restoreRNG;
! 4559: return y;
! 4560: }
! 4561:
! 4562: void describeHallucinatedItem(char *buf) {
! 4563: const unsigned short itemCats[10] = {FOOD, WEAPON, ARMOR, POTION, SCROLL, STAFF, WAND, RING, CHARM, GOLD};
! 4564: short cat, kind, maxKinds;
! 4565: assureCosmeticRNG;
! 4566: cat = itemCats[rand_range(0, 9)];
! 4567: tableForItemCategory(cat, &maxKinds);
! 4568: kind = rand_range(0, maxKinds - 1);
! 4569: describedItemBasedOnParameters(cat, kind, 1, 1, buf);
! 4570: restoreRNG;
! 4571: }
! 4572:
! 4573: // Returns the y-coordinate after the last line printed.
! 4574: short printItemInfo(item *theItem, short y, boolean dim, boolean highlight) {
! 4575: char name[COLS * 3];
! 4576: enum displayGlyph itemChar;
! 4577: color itemForeColor, itemBackColor;
! 4578: short initialY, i, j, highlightStrength, lineCount;
! 4579: boolean inPath;
! 4580: short oldRNG;
! 4581:
! 4582: if (y >= ROWS - 1) {
! 4583: return ROWS - 1;
! 4584: }
! 4585:
! 4586: initialY = y;
! 4587:
! 4588: oldRNG = rogue.RNG;
! 4589: rogue.RNG = RNG_COSMETIC;
! 4590: //assureCosmeticRNG;
! 4591:
! 4592: if (y < ROWS - 1) {
! 4593: // Unhighlight if it's highlighted as part of the path.
! 4594: inPath = (pmap[theItem->xLoc][theItem->yLoc].flags & IS_IN_PATH) ? true : false;
! 4595: pmap[theItem->xLoc][theItem->yLoc].flags &= ~IS_IN_PATH;
! 4596: getCellAppearance(theItem->xLoc, theItem->yLoc, &itemChar, &itemForeColor, &itemBackColor);
! 4597: applyColorBounds(&itemForeColor, 0, 100);
! 4598: applyColorBounds(&itemBackColor, 0, 100);
! 4599: if (inPath) {
! 4600: pmap[theItem->xLoc][theItem->yLoc].flags |= IS_IN_PATH;
! 4601: }
! 4602: if (dim) {
! 4603: applyColorAverage(&itemForeColor, &black, 50);
! 4604: applyColorAverage(&itemBackColor, &black, 50);
! 4605: }
! 4606: plotCharWithColor(itemChar, 0, y, &itemForeColor, &itemBackColor);
! 4607: printString(": ", 1, y, (dim ? &gray : &white), &black, 0);
! 4608: if (rogue.playbackOmniscience || !player.status[STATUS_HALLUCINATING]) {
! 4609: itemName(theItem, name, true, true, (dim ? &gray : &white));
! 4610: } else {
! 4611: describeHallucinatedItem(name);
! 4612: }
! 4613: upperCase(name);
! 4614: lineCount = wrapText(NULL, name, 20-3);
! 4615: for (i=initialY + 1; i <= initialY + lineCount + 1 && i < ROWS - 1; i++) {
! 4616: printString(" ", 0, i, (dim ? &darkGray : &gray), &black, 0);
! 4617: }
! 4618: y = printStringWithWrapping(name, 3, y, 20-3, (dim ? &gray : &white), &black, NULL); // Advances y.
! 4619: }
! 4620:
! 4621: if (highlight) {
! 4622: for (i=0; i<20; i++) {
! 4623: highlightStrength = smoothHiliteGradient(i, 20-1) / 10;
! 4624: for (j=initialY; j <= y && j < ROWS - 1; j++) {
! 4625: highlightScreenCell(i, j, &white, highlightStrength);
! 4626: }
! 4627: }
! 4628: }
! 4629: y += 2;
! 4630:
! 4631: restoreRNG;
! 4632: return y;
! 4633: }
! 4634:
! 4635: // Returns the y-coordinate after the last line printed.
! 4636: short printTerrainInfo(short x, short y, short py, const char *description, boolean dim, boolean highlight) {
! 4637: enum displayGlyph displayChar;
! 4638: color foreColor, backColor;
! 4639: short initialY, i, j, highlightStrength, lineCount;
! 4640: boolean inPath;
! 4641: char name[DCOLS*2];
! 4642: color textColor;
! 4643: short oldRNG;
! 4644:
! 4645: if (py >= ROWS - 1) {
! 4646: return ROWS - 1;
! 4647: }
! 4648:
! 4649: initialY = py;
! 4650:
! 4651: oldRNG = rogue.RNG;
! 4652: rogue.RNG = RNG_COSMETIC;
! 4653: //assureCosmeticRNG;
! 4654:
! 4655: if (py < ROWS - 1) {
! 4656: // Unhighlight if it's highlighted as part of the path.
! 4657: inPath = (pmap[x][y].flags & IS_IN_PATH) ? true : false;
! 4658: pmap[x][y].flags &= ~IS_IN_PATH;
! 4659: getCellAppearance(x, y, &displayChar, &foreColor, &backColor);
! 4660: applyColorBounds(&foreColor, 0, 100);
! 4661: applyColorBounds(&backColor, 0, 100);
! 4662: if (inPath) {
! 4663: pmap[x][y].flags |= IS_IN_PATH;
! 4664: }
! 4665: if (dim) {
! 4666: applyColorAverage(&foreColor, &black, 50);
! 4667: applyColorAverage(&backColor, &black, 50);
! 4668: }
! 4669: plotCharWithColor(displayChar, 0, py, &foreColor, &backColor);
! 4670: printString(": ", 1, py, (dim ? &gray : &white), &black, 0);
! 4671: strcpy(name, description);
! 4672: upperCase(name);
! 4673: lineCount = wrapText(NULL, name, 20-3);
! 4674: for (i=initialY + 1; i <= initialY + lineCount + 1 && i < ROWS - 1; i++) {
! 4675: printString(" ", 0, i, (dim ? &darkGray : &gray), &black, 0);
! 4676: }
! 4677: textColor = flavorTextColor;
! 4678: if (dim) {
! 4679: applyColorScalar(&textColor, 50);
! 4680: }
! 4681: py = printStringWithWrapping(name, 3, py, 20-3, &textColor, &black, NULL); // Advances y.
! 4682: }
! 4683:
! 4684: if (highlight) {
! 4685: for (i=0; i<20; i++) {
! 4686: highlightStrength = smoothHiliteGradient(i, 20-1) / 10;
! 4687: for (j=initialY; j <= py && j < ROWS - 1; j++) {
! 4688: highlightScreenCell(i, j, &white, highlightStrength);
! 4689: }
! 4690: }
! 4691: }
! 4692: py += 2;
! 4693:
! 4694: restoreRNG;
! 4695: return py;
! 4696: }
! 4697:
! 4698: void rectangularShading(short x, short y, short width, short height,
! 4699: const color *backColor, short opacity, cellDisplayBuffer dbuf[COLS][ROWS]) {
! 4700: short i, j, dist;
! 4701:
! 4702: assureCosmeticRNG;
! 4703: for (i=0; i<COLS; i++) {
! 4704: for (j=0; j<ROWS; j++) {
! 4705: storeColorComponents(dbuf[i][j].backColorComponents, backColor);
! 4706:
! 4707: if (i >= x && i < x + width
! 4708: && j >= y && j < y + height) {
! 4709: dbuf[i][j].opacity = min(100, opacity);
! 4710: } else {
! 4711: dist = 0;
! 4712: dist += max(0, max(x - i, i - x - width + 1));
! 4713: dist += max(0, max(y - j, j - y - height + 1));
! 4714: dbuf[i][j].opacity = (int) ((opacity - 10) / max(1, dist));
! 4715: if (dbuf[i][j].opacity < 3) {
! 4716: dbuf[i][j].opacity = 0;
! 4717: }
! 4718: }
! 4719: }
! 4720: }
! 4721:
! 4722: // for (i=0; i<COLS; i++) {
! 4723: // for (j=0; j<ROWS; j++) {
! 4724: // if (i >= x && i < x + width && j >= y && j < y + height) {
! 4725: // plotCharWithColor(' ', i, j, &white, &darkGreen);
! 4726: // }
! 4727: // }
! 4728: // }
! 4729: // displayMoreSign();
! 4730:
! 4731: restoreRNG;
! 4732: }
! 4733:
! 4734: #define MIN_DEFAULT_INFO_PANEL_WIDTH 33
! 4735:
! 4736: // y and width are optional and will be automatically calculated if width <= 0.
! 4737: // Width will automatically be widened if the text would otherwise fall off the bottom of the
! 4738: // screen, and x will be adjusted to keep the widening box from spilling off the right of the
! 4739: // screen.
! 4740: // If buttons are provided, we'll extend the text box downward, re-position the buttons,
! 4741: // run a button input loop and return the result.
! 4742: // (Returns -1 for canceled; otherwise the button index number.)
! 4743: short printTextBox(char *textBuf, short x, short y, short width,
! 4744: color *foreColor, color *backColor,
! 4745: cellDisplayBuffer rbuf[COLS][ROWS],
! 4746: brogueButton *buttons, short buttonCount) {
! 4747: cellDisplayBuffer dbuf[COLS][ROWS];
! 4748:
! 4749: short x2, y2, lineCount, i, bx, by, padLines;
! 4750:
! 4751: if (width <= 0) {
! 4752: // autocalculate y and width
! 4753: if (x < DCOLS / 2 - 1) {
! 4754: x2 = mapToWindowX(x + 10);
! 4755: width = (DCOLS - x) - 20;
! 4756: } else {
! 4757: x2 = mapToWindowX(10);
! 4758: width = x - 20;
! 4759: }
! 4760: y2 = mapToWindowY(2);
! 4761:
! 4762: if (width < MIN_DEFAULT_INFO_PANEL_WIDTH) {
! 4763: x2 -= (MIN_DEFAULT_INFO_PANEL_WIDTH - width) / 2;
! 4764: width = MIN_DEFAULT_INFO_PANEL_WIDTH;
! 4765: }
! 4766: } else {
! 4767: y2 = y;
! 4768: x2 = x;
! 4769: }
! 4770:
! 4771: while (((lineCount = wrapText(NULL, textBuf, width)) + y2) >= ROWS - 2 && width < COLS-5) {
! 4772: // While the text doesn't fit and the width doesn't fill the screen, increase the width.
! 4773: width++;
! 4774: if (x2 + (width / 2) > COLS / 2) {
! 4775: // If the horizontal midpoint of the text box is on the right half of the screen,
! 4776: // move the box one space to the left.
! 4777: x2--;
! 4778: }
! 4779: }
! 4780:
! 4781: if (buttonCount > 0) {
! 4782: padLines = 2;
! 4783: bx = x2 + width;
! 4784: by = y2 + lineCount + 1;
! 4785: for (i=0; i<buttonCount; i++) {
! 4786: if (buttons[i].flags & B_DRAW) {
! 4787: bx -= strLenWithoutEscapes(buttons[i].text) + 2;
! 4788: buttons[i].x = bx;
! 4789: buttons[i].y = by;
! 4790: if (bx < x2) {
! 4791: // Buttons can wrap to the next line (though are double-spaced).
! 4792: bx = x2 + width - (strLenWithoutEscapes(buttons[i].text) + 2);
! 4793: by += 2;
! 4794: padLines += 2;
! 4795: buttons[i].x = bx;
! 4796: buttons[i].y = by;
! 4797: }
! 4798: }
! 4799: }
! 4800: } else {
! 4801: padLines = 0;
! 4802: }
! 4803:
! 4804: clearDisplayBuffer(dbuf);
! 4805: printStringWithWrapping(textBuf, x2, y2, width, foreColor, backColor, dbuf);
! 4806: rectangularShading(x2, y2, width, lineCount + padLines, backColor, INTERFACE_OPACITY, dbuf);
! 4807: overlayDisplayBuffer(dbuf, rbuf);
! 4808:
! 4809: if (buttonCount > 0) {
! 4810: return buttonInputLoop(buttons, buttonCount, x2, y2, width, by - y2 + 1 + padLines, NULL);
! 4811: } else {
! 4812: return -1;
! 4813: }
! 4814: }
! 4815:
! 4816: void printMonsterDetails(creature *monst, cellDisplayBuffer rbuf[COLS][ROWS]) {
! 4817: char textBuf[COLS * 100];
! 4818:
! 4819: monsterDetails(textBuf, monst);
! 4820: printTextBox(textBuf, monst->xLoc, 0, 0, &white, &black, rbuf, NULL, 0);
! 4821: }
! 4822:
! 4823: // Displays the item info box with the dark blue background.
! 4824: // If includeButtons is true, we include buttons for item actions.
! 4825: // Returns the key of an action to take, if any; otherwise -1.
! 4826: unsigned long printCarriedItemDetails(item *theItem,
! 4827: short x, short y, short width,
! 4828: boolean includeButtons,
! 4829: cellDisplayBuffer rbuf[COLS][ROWS]) {
! 4830: char textBuf[COLS * 100], goldColorEscape[5] = "", whiteColorEscape[5] = "";
! 4831: brogueButton buttons[20] = {{{0}}};
! 4832: short b;
! 4833:
! 4834: itemDetails(textBuf, theItem);
! 4835:
! 4836: for (b=0; b<20; b++) {
! 4837: initializeButton(&(buttons[b]));
! 4838: buttons[b].flags |= B_WIDE_CLICK_AREA;
! 4839: }
! 4840:
! 4841: b = 0;
! 4842: if (includeButtons) {
! 4843: encodeMessageColor(goldColorEscape, 0, KEYBOARD_LABELS ? &yellow : &white);
! 4844: encodeMessageColor(whiteColorEscape, 0, &white);
! 4845:
! 4846: if (theItem->category & (FOOD | SCROLL | POTION | WAND | STAFF | CHARM)) {
! 4847: sprintf(buttons[b].text, " %sa%spply ", goldColorEscape, whiteColorEscape);
! 4848: buttons[b].hotkey[0] = APPLY_KEY;
! 4849: b++;
! 4850: }
! 4851: if (theItem->category & (ARMOR | WEAPON | RING)) {
! 4852: if (theItem->flags & ITEM_EQUIPPED) {
! 4853: sprintf(buttons[b].text, " %sr%semove ", goldColorEscape, whiteColorEscape);
! 4854: buttons[b].hotkey[0] = UNEQUIP_KEY;
! 4855: b++;
! 4856: } else {
! 4857: sprintf(buttons[b].text, " %se%squip ", goldColorEscape, whiteColorEscape);
! 4858: buttons[b].hotkey[0] = EQUIP_KEY;
! 4859: b++;
! 4860: }
! 4861: }
! 4862: sprintf(buttons[b].text, " %sd%srop ", goldColorEscape, whiteColorEscape);
! 4863: buttons[b].hotkey[0] = DROP_KEY;
! 4864: b++;
! 4865:
! 4866: sprintf(buttons[b].text, " %st%shrow ", goldColorEscape, whiteColorEscape);
! 4867: buttons[b].hotkey[0] = THROW_KEY;
! 4868: b++;
! 4869:
! 4870: if (itemCanBeCalled(theItem)) {
! 4871: sprintf(buttons[b].text, " %sc%sall ", goldColorEscape, whiteColorEscape);
! 4872: buttons[b].hotkey[0] = CALL_KEY;
! 4873: b++;
! 4874: }
! 4875:
! 4876: if (KEYBOARD_LABELS) {
! 4877: sprintf(buttons[b].text, " %sR%selabel ", goldColorEscape, whiteColorEscape);
! 4878: buttons[b].hotkey[0] = RELABEL_KEY;
! 4879: b++;
! 4880: }
! 4881:
! 4882: // Add invisible previous and next buttons, so up and down arrows can page through items.
! 4883: // Previous
! 4884: buttons[b].flags = B_ENABLED; // clear everything else
! 4885: buttons[b].hotkey[0] = UP_KEY;
! 4886: buttons[b].hotkey[1] = NUMPAD_8;
! 4887: buttons[b].hotkey[2] = UP_ARROW;
! 4888: b++;
! 4889: // Next
! 4890: buttons[b].flags = B_ENABLED; // clear everything else
! 4891: buttons[b].hotkey[0] = DOWN_KEY;
! 4892: buttons[b].hotkey[1] = NUMPAD_2;
! 4893: buttons[b].hotkey[2] = DOWN_ARROW;
! 4894: b++;
! 4895: }
! 4896: b = printTextBox(textBuf, x, y, width, &white, &interfaceBoxColor, rbuf, buttons, b);
! 4897:
! 4898: if (!includeButtons) {
! 4899: waitForKeystrokeOrMouseClick();
! 4900: return -1;
! 4901: }
! 4902:
! 4903: if (b >= 0) {
! 4904: return buttons[b].hotkey[0];
! 4905: } else {
! 4906: return -1;
! 4907: }
! 4908: }
! 4909:
! 4910: // Returns true if an action was taken.
! 4911: void printFloorItemDetails(item *theItem, cellDisplayBuffer rbuf[COLS][ROWS]) {
! 4912: char textBuf[COLS * 100];
! 4913:
! 4914: itemDetails(textBuf, theItem);
! 4915: printTextBox(textBuf, theItem->xLoc, 0, 0, &white, &black, rbuf, NULL, 0);
! 4916: }
CVSweb