1 /* $NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Timothy C. Stoehr. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)room.c 8.1 (Berkeley) 5/31/93"; 39 #else 40 __RCSID("$NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 /* 45 * room.c 46 * 47 * This source herein may be modified and/or distributed by anybody who 48 * so desires, with the following restrictions: 49 * 1.) No portion of this notice shall be removed. 50 * 2.) Credit shall not be taken for the creation of this source. 51 * 3.) This code is not to be traded, sold, or used for personal 52 * gain or profit. 53 * 54 */ 55 56 #include "rogue.h" 57 58 room rooms[MAXROOMS]; 59 60 static boolean rooms_visited[MAXROOMS]; 61 62 #define NOPTS 7 63 static const struct option { 64 const char *prompt; 65 boolean is_bool; 66 char **strval; 67 boolean *bval; 68 } options[NOPTS] = { 69 { 70 "Show position only at end of run (\"jump\"): ", 71 1, NULL, &jump 72 }, 73 { 74 "Follow turnings in passageways (\"passgo\"): ", 75 1, NULL, &passgo 76 }, 77 { 78 "Don't print skull when killed (\"noskull\" or \"notombstone\"): ", 79 1, NULL, &no_skull 80 }, 81 { 82 "Ask player before saying 'Okay, bye-bye!' (\"askquit\"): ", 83 1, NULL, &ask_quit 84 }, 85 { 86 "Name (\"name\"): ", 87 0, &nick_name, NULL 88 }, 89 { 90 "Fruit (\"fruit\"): ", 91 0, &fruit, NULL 92 }, 93 { 94 "Save file (\"file\"): ", 95 0, &save_file, NULL 96 } 97 }; 98 99 static boolean get_oth_room(short, short *, short *); 100 static void opt_erase(int); 101 static void opt_go(int); 102 static void opt_show(int); 103 static void visit_rooms(int); 104 105 void 106 light_up_room(int rn) 107 { 108 short i, j; 109 110 if (!blind) { 111 for (i = rooms[rn].top_row; 112 i <= rooms[rn].bottom_row; i++) { 113 for (j = rooms[rn].left_col; 114 j <= rooms[rn].right_col; j++) { 115 if (dungeon[i][j] & MONSTER) { 116 object *monster; 117 118 if ((monster = object_at( 119 &level_monsters, i, j)) != NULL) { 120 dungeon[monster->row][monster->col] &= (~MONSTER); 121 monster->trail_char = 122 get_dungeon_char(monster->row, monster->col); 123 dungeon[monster->row][monster->col] |= MONSTER; 124 } 125 } 126 mvaddch(i, j, get_dungeon_char(i, j)); 127 } 128 } 129 mvaddch(rogue.row, rogue.col, rogue.fchar); 130 } 131 } 132 133 void 134 light_passage(int row, int col) 135 { 136 short i, j, i_end, j_end; 137 138 if (blind) { 139 return; 140 } 141 i_end = (row < (DROWS-2)) ? 1 : 0; 142 j_end = (col < (DCOLS-1)) ? 1 : 0; 143 144 for (i = ((row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { 145 for (j = ((col > 0) ? -1 : 0); j <= j_end; j++) { 146 if (can_move(row, col, row+i, col+j)) { 147 mvaddch(row+i, col+j, get_dungeon_char(row+i, col+j)); 148 } 149 } 150 } 151 } 152 153 void 154 darken_room(short rn) 155 { 156 short i, j; 157 158 for (i = rooms[rn].top_row + 1; i < rooms[rn].bottom_row; i++) { 159 for (j = rooms[rn].left_col + 1; j < rooms[rn].right_col; j++) { 160 if (blind) { 161 mvaddch(i, j, ' '); 162 } else { 163 if (!(dungeon[i][j] & (OBJECT | STAIRS)) && 164 !(detect_monster && (dungeon[i][j] & MONSTER))) { 165 if (!imitating(i, j)) { 166 mvaddch(i, j, ' '); 167 } 168 if ((dungeon[i][j] & TRAP) && (!(dungeon[i][j] & HIDDEN))) { 169 mvaddch(i, j, '^'); 170 } 171 } 172 } 173 } 174 } 175 } 176 177 char 178 get_dungeon_char(short row, short col) 179 { 180 unsigned short mask = dungeon[row][col]; 181 182 if (mask & MONSTER) { 183 return(gmc_row_col(row, col)); 184 } 185 if (mask & OBJECT) { 186 object *obj; 187 188 obj = object_at(&level_objects, row, col); 189 return(get_mask_char(obj->what_is)); 190 } 191 if (mask & (TUNNEL | STAIRS | HORWALL | VERTWALL | FLOOR | DOOR)) { 192 if ((mask & (TUNNEL| STAIRS)) && (!(mask & HIDDEN))) { 193 return(((mask & STAIRS) ? '%' : '#')); 194 } 195 if (mask & HORWALL) { 196 return('-'); 197 } 198 if (mask & VERTWALL) { 199 return('|'); 200 } 201 if (mask & FLOOR) { 202 if (mask & TRAP) { 203 if (!(dungeon[row][col] & HIDDEN)) { 204 return('^'); 205 } 206 } 207 return('.'); 208 } 209 if (mask & DOOR) { 210 if (mask & HIDDEN) { 211 if (((col > 0) && (dungeon[row][col-1] & HORWALL)) || 212 ((col < (DCOLS-1)) && (dungeon[row][col+1] & HORWALL))) { 213 return('-'); 214 } else { 215 return('|'); 216 } 217 } else { 218 return('+'); 219 } 220 } 221 } 222 return(' '); 223 } 224 225 char 226 get_mask_char(unsigned short mask) 227 { 228 switch(mask) { 229 case SCROL: 230 return('?'); 231 case POTION: 232 return('!'); 233 case GOLD: 234 return('*'); 235 case FOOD: 236 return(':'); 237 case WAND: 238 return('/'); 239 case ARMOR: 240 return(']'); 241 case WEAPON: 242 return(')'); 243 case RING: 244 return('='); 245 case AMULET: 246 return(','); 247 default: 248 return('~'); /* unknown, something is wrong */ 249 } 250 } 251 252 void 253 gr_row_col(short *row, short *col, unsigned short mask) 254 { 255 short rn; 256 short r, c; 257 258 do { 259 r = get_rand(MIN_ROW, DROWS-2); 260 c = get_rand(0, DCOLS-1); 261 rn = get_room_number(r, c); 262 } while ((rn == NO_ROOM) || 263 (!(dungeon[r][c] & mask)) || 264 (dungeon[r][c] & (~mask)) || 265 (!(rooms[rn].is_room & (R_ROOM | R_MAZE))) || 266 ((r == rogue.row) && (c == rogue.col))); 267 268 *row = r; 269 *col = c; 270 } 271 272 short 273 gr_room(void) 274 { 275 short i; 276 277 do { 278 i = get_rand(0, MAXROOMS-1); 279 } while (!(rooms[i].is_room & (R_ROOM | R_MAZE))); 280 281 return(i); 282 } 283 284 short 285 party_objects(int rn) 286 { 287 short i, j, nf = 0; 288 object *obj; 289 short n, N, row, col; 290 boolean found; 291 292 row = col = 0; 293 N = ((rooms[rn].bottom_row - rooms[rn].top_row) - 1) * 294 ((rooms[rn].right_col - rooms[rn].left_col) - 1); 295 n = get_rand(5, 10); 296 if (n > N) { 297 n = N - 2; 298 } 299 for (i = 0; i < n; i++) { 300 for (j = found = 0; ((!found) && (j < 250)); j++) { 301 row = get_rand(rooms[rn].top_row+1, 302 rooms[rn].bottom_row-1); 303 col = get_rand(rooms[rn].left_col+1, 304 rooms[rn].right_col-1); 305 if ((dungeon[row][col] == FLOOR) || (dungeon[row][col] == TUNNEL)) { 306 found = 1; 307 } 308 } 309 if (found) { 310 obj = gr_object(); 311 place_at(obj, row, col); 312 nf++; 313 } 314 } 315 return(nf); 316 } 317 318 short 319 get_room_number(int row, int col) 320 { 321 short i; 322 323 for (i = 0; i < MAXROOMS; i++) { 324 if ((row >= rooms[i].top_row) && (row <= rooms[i].bottom_row) && 325 (col >= rooms[i].left_col) && (col <= rooms[i].right_col)) { 326 return(i); 327 } 328 } 329 return(NO_ROOM); 330 } 331 332 boolean 333 is_all_connected(void) 334 { 335 short i, starting_room; 336 337 starting_room = 0; 338 for (i = 0; i < MAXROOMS; i++) { 339 rooms_visited[i] = 0; 340 if (rooms[i].is_room & (R_ROOM | R_MAZE)) { 341 starting_room = i; 342 } 343 } 344 345 visit_rooms(starting_room); 346 347 for (i = 0; i < MAXROOMS; i++) { 348 if ((rooms[i].is_room & (R_ROOM | R_MAZE)) && (!rooms_visited[i])) { 349 return(0); 350 } 351 } 352 return(1); 353 } 354 355 static void 356 visit_rooms(int rn) 357 { 358 short i; 359 short oth_rn; 360 361 rooms_visited[rn] = 1; 362 363 for (i = 0; i < 4; i++) { 364 oth_rn = rooms[rn].doors[i].oth_room; 365 if ((oth_rn >= 0) && (!rooms_visited[oth_rn])) { 366 visit_rooms(oth_rn); 367 } 368 } 369 } 370 371 void 372 draw_magic_map(void) 373 { 374 short i, j, ch, och; 375 unsigned short mask = (HORWALL | VERTWALL | DOOR | TUNNEL | TRAP | STAIRS | 376 MONSTER); 377 unsigned short s; 378 379 for (i = 0; i < DROWS; i++) { 380 for (j = 0; j < DCOLS; j++) { 381 s = dungeon[i][j]; 382 if (s & mask) { 383 if (((ch = mvinch(i, j)) == ' ') || 384 ((ch >= 'A') && (ch <= 'Z')) || (s & (TRAP | HIDDEN))) { 385 och = ch; 386 dungeon[i][j] &= (~HIDDEN); 387 if (s & HORWALL) { 388 ch = '-'; 389 } else if (s & VERTWALL) { 390 ch = '|'; 391 } else if (s & DOOR) { 392 ch = '+'; 393 } else if (s & TRAP) { 394 ch = '^'; 395 } else if (s & STAIRS) { 396 ch = '%'; 397 } else if (s & TUNNEL) { 398 ch = '#'; 399 } else { 400 continue; 401 } 402 if ((!(s & MONSTER)) || (och == ' ')) { 403 addch(ch); 404 } 405 if (s & MONSTER) { 406 object *monster; 407 408 if ((monster = object_at( 409 &level_monsters, i, j)) 410 != NULL) { 411 monster->trail_char = 412 ch; 413 } 414 } 415 } 416 } 417 } 418 } 419 } 420 421 void 422 dr_course(object *monster, boolean entering, short row, short col) 423 { 424 short i, j, k, rn; 425 short r, rr; 426 427 monster->row = row; 428 monster->col = col; 429 430 if (mon_sees(monster, rogue.row, rogue.col)) { 431 monster->trow = NO_ROOM; 432 return; 433 } 434 rn = get_room_number(row, col); 435 436 if (entering) { /* entering room */ 437 /* look for door to some other room */ 438 r = get_rand(0, MAXROOMS-1); 439 for (i = 0; i < MAXROOMS; i++) { 440 rr = (r + i) % MAXROOMS; 441 if ((!(rooms[rr].is_room & (R_ROOM | R_MAZE))) || (rr == rn)) { 442 continue; 443 } 444 for (k = 0; k < 4; k++) { 445 if (rooms[rr].doors[k].oth_room == rn) { 446 monster->trow = rooms[rr].doors[k].oth_row; 447 monster->tcol = rooms[rr].doors[k].oth_col; 448 if ((monster->trow == row) && 449 (monster->tcol == col)) { 450 continue; 451 } 452 return; 453 } 454 } 455 } 456 /* look for door to dead end */ 457 if (rn == NO_ROOM) 458 clean_up("dr_course: monster not in room"); 459 for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { 460 for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { 461 if ((i != monster->row) && (j != monster->col) && 462 (dungeon[i][j] & DOOR)) { 463 monster->trow = i; 464 monster->tcol = j; 465 return; 466 } 467 } 468 } 469 /* return monster to room that he came from */ 470 for (i = 0; i < MAXROOMS; i++) { 471 for (j = 0; j < 4; j++) { 472 if (rooms[i].doors[j].oth_room == rn) { 473 for (k = 0; k < 4; k++) { 474 if (rooms[rn].doors[k].oth_room == i) { 475 monster->trow = rooms[rn].doors[k].oth_row; 476 monster->tcol = rooms[rn].doors[k].oth_col; 477 return; 478 } 479 } 480 } 481 } 482 } 483 /* no place to send monster */ 484 monster->trow = NO_ROOM; 485 } else { /* exiting room */ 486 if (rn == NO_ROOM || !get_oth_room(rn, &row, &col)) { 487 monster->trow = NO_ROOM; 488 } else { 489 monster->trow = row; 490 monster->tcol = col; 491 } 492 } 493 } 494 495 static boolean 496 get_oth_room(short rn, short *row, short *col) 497 { 498 short d = -1; 499 500 if (*row == rooms[rn].top_row) { 501 d = UPWARD/2; 502 } else if (*row == rooms[rn].bottom_row) { 503 d = DOWN/2; 504 } else if (*col == rooms[rn].left_col) { 505 d = LEFT/2; 506 } else if (*col == rooms[rn].right_col) { 507 d = RIGHT/2; 508 } 509 if ((d != -1) && (rooms[rn].doors[d].oth_room >= 0)) { 510 *row = rooms[rn].doors[d].oth_row; 511 *col = rooms[rn].doors[d].oth_col; 512 return(1); 513 } 514 return(0); 515 } 516 517 void 518 edit_opts(void) 519 { 520 char save[NOPTS+1][DCOLS]; 521 short i, j; 522 short ch; 523 boolean done = 0; 524 char buf[MAX_OPT_LEN + 2]; 525 526 for (i = 0; i < NOPTS+1; i++) { 527 for (j = 0; j < DCOLS; j++) { 528 save[i][j] = mvinch(i, j); 529 } 530 if (i < NOPTS) { 531 opt_show(i); 532 } 533 } 534 opt_go(0); 535 i = 0; 536 537 while (!done) { 538 refresh(); 539 ch = rgetchar(); 540 CH: 541 switch(ch) { 542 case '\033': 543 done = 1; 544 break; 545 case '\012': 546 case '\015': 547 if (i == (NOPTS - 1)) { 548 mvaddstr(NOPTS, 0, press_space); 549 refresh(); 550 wait_for_ack(); 551 done = 1; 552 } else { 553 i++; 554 opt_go(i); 555 } 556 break; 557 case '-': 558 if (i > 0) { 559 opt_go(--i); 560 } else { 561 sound_bell(); 562 } 563 break; 564 case 't': 565 case 'T': 566 case 'f': 567 case 'F': 568 if (options[i].is_bool) { 569 *(options[i].bval) = (((ch == 't') || (ch == 'T')) ? 1 : 0); 570 opt_show(i); 571 opt_go(++i); 572 break; 573 } 574 default: 575 if (options[i].is_bool) { 576 sound_bell(); 577 break; 578 } 579 j = 0; 580 if ((ch == '\010') || ((ch >= ' ') && (ch <= '~'))) { 581 opt_erase(i); 582 do { 583 if ((ch >= ' ') && (ch <= '~') && (j < MAX_OPT_LEN)) { 584 buf[j++] = ch; 585 buf[j] = '\0'; 586 addch(ch); 587 } else if ((ch == '\010') && (j > 0)) { 588 buf[--j] = '\0'; 589 move(i, j + strlen(options[i].prompt)); 590 addch(' '); 591 move(i, j + strlen(options[i].prompt)); 592 } 593 refresh(); 594 ch = rgetchar(); 595 } while ((ch != '\012') && (ch != '\015') && (ch != '\033')); 596 if (j != 0) { 597 /* 598 * We rely on the option string being 599 * allocated to hold MAX_OPT_LEN+2 600 * bytes. This is arranged in init.c. 601 */ 602 (void)strcpy(*(options[i].strval), buf); 603 } 604 opt_show(i); 605 goto CH; 606 } else { 607 sound_bell(); 608 } 609 break; 610 } 611 } 612 613 for (i = 0; i < NOPTS+1; i++) { 614 move(i, 0); 615 for (j = 0; j < DCOLS; j++) { 616 addch(save[i][j]); 617 } 618 } 619 } 620 621 static void 622 opt_show(int i) 623 { 624 const char *s; 625 const struct option *opt = &options[i]; 626 627 opt_erase(i); 628 629 if (opt->is_bool) { 630 s = *(opt->bval) ? "True" : "False"; 631 } else { 632 s = *(opt->strval); 633 } 634 addstr(s); 635 } 636 637 static void 638 opt_erase(int i) 639 { 640 const struct option *opt = &options[i]; 641 642 mvaddstr(i, 0, opt->prompt); 643 clrtoeol(); 644 } 645 646 static void 647 opt_go(int i) 648 { 649 move(i, strlen(options[i].prompt)); 650 } 651 652 void 653 do_shell(void) 654 { 655 #ifdef UNIX 656 const char *sh; 657 658 md_ignore_signals(); 659 if (!(sh = md_getenv("SHELL"))) { 660 sh = "/bin/sh"; 661 } 662 move(LINES-1, 0); 663 refresh(); 664 stop_window(); 665 printf("\nCreating new shell...\n"); 666 md_shell(sh); 667 start_window(); 668 wrefresh(curscr); 669 md_heed_signals(); 670 #endif 671 } 672