1 /*- 2 * Copyright (c) 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Timothy C. Stoehr. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)move.c 8.1 (Berkeley) 5/31/93 33 * $FreeBSD: src/games/rogue/move.c,v 1.7 1999/11/30 03:49:24 billf Exp $ 34 */ 35 36 /* 37 * move.c 38 * 39 * This source herein may be modified and/or distributed by anybody who 40 * so desires, with the following restrictions: 41 * 1.) No portion of this notice shall be removed. 42 * 2.) Credit shall not be taken for the creation of this source. 43 * 3.) This code is not to be traded, sold, or used for personal 44 * gain or profit. 45 * 46 */ 47 48 #include "rogue.h" 49 50 short m_moves = 0; 51 boolean jump = 0; 52 const char you_can_move_again[] = "you can move again"; 53 54 static boolean can_turn(short, short); 55 static boolean check_hunger(boolean); 56 static short gr_dir(void); 57 static void heal(void); 58 static boolean next_to_something(int, int); 59 static void turn_passage(short, boolean); 60 61 short 62 one_move_rogue(short dirch, short pickup) 63 { 64 short row, col; 65 object *obj; 66 char desc[DCOLS]; 67 short n, status, d; 68 69 row = rogue.row; 70 col = rogue.col; 71 72 if (confused) { 73 dirch = gr_dir(); 74 } 75 is_direction(dirch, &d); 76 get_dir_rc(d, &row, &col, 1); 77 78 if (!can_move(rogue.row, rogue.col, row, col)) { 79 return(MOVE_FAILED); 80 } 81 if (being_held || bear_trap) { 82 if (!(dungeon[row][col] & MONSTER)) { 83 if (being_held) { 84 message("you are being held", 1); 85 } else { 86 message("you are still stuck in the bear trap", 0); 87 reg_move(); 88 } 89 return(MOVE_FAILED); 90 } 91 } 92 if (r_teleport) { 93 if (rand_percent(R_TELE_PERCENT)) { 94 tele(); 95 return(STOPPED_ON_SOMETHING); 96 } 97 } 98 if (dungeon[row][col] & MONSTER) { 99 rogue_hit(object_at(&level_monsters, row, col), 0); 100 reg_move(); 101 return(MOVE_FAILED); 102 } 103 if (dungeon[row][col] & DOOR) { 104 if (cur_room == PASSAGE) { 105 cur_room = get_room_number(row, col); 106 if (cur_room == NO_ROOM) 107 clean_up("one_move_rogue: door to nowhere"); 108 light_up_room(cur_room); 109 wake_room(cur_room, 1, row, col); 110 } else { 111 light_passage(row, col); 112 } 113 } else if ((dungeon[rogue.row][rogue.col] & DOOR) && 114 (dungeon[row][col] & TUNNEL)) { 115 light_passage(row, col); 116 wake_room(cur_room, 0, rogue.row, rogue.col); 117 darken_room(cur_room); 118 cur_room = PASSAGE; 119 } else if (dungeon[row][col] & TUNNEL) { 120 light_passage(row, col); 121 } 122 mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col)); 123 mvaddch(row, col, rogue.fchar); 124 125 if (!jump) { 126 refresh(); 127 } 128 rogue.row = row; 129 rogue.col = col; 130 if (dungeon[row][col] & OBJECT) { 131 if (levitate && pickup) { 132 return(STOPPED_ON_SOMETHING); 133 } 134 if (pickup && !levitate) { 135 if ((obj = pick_up(row, col, &status)) != NULL) { 136 get_desc(obj, desc); 137 if (obj->what_is == GOLD) { 138 free_object(obj); 139 goto NOT_IN_PACK; 140 } 141 } else if (!status) { 142 goto MVED; 143 } else { 144 goto MOVE_ON; 145 } 146 } else { 147 MOVE_ON: 148 obj = object_at(&level_objects, row, col); 149 strcpy(desc, "moved onto "); 150 get_desc(obj, desc+11); 151 goto NOT_IN_PACK; 152 } 153 n = strlen(desc); 154 desc[n] = '('; 155 desc[n+1] = obj->ichar; 156 desc[n+2] = ')'; 157 desc[n+3] = 0; 158 NOT_IN_PACK: 159 message(desc, 1); 160 reg_move(); 161 return(STOPPED_ON_SOMETHING); 162 } 163 if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) { 164 if ((!levitate) && (dungeon[row][col] & TRAP)) { 165 trap_player(row, col); 166 } 167 reg_move(); 168 return(STOPPED_ON_SOMETHING); 169 } 170 MVED: if (reg_move()) { /* fainted from hunger */ 171 return(STOPPED_ON_SOMETHING); 172 } 173 return((confused ? STOPPED_ON_SOMETHING : MOVED)); 174 } 175 176 void 177 multiple_move_rogue(short dirch) 178 { 179 short row, col; 180 short m; 181 182 switch(dirch) { 183 case '\010': 184 case '\012': 185 case '\013': 186 case '\014': 187 case '\031': 188 case '\025': 189 case '\016': 190 case '\002': 191 do { 192 row = rogue.row; 193 col = rogue.col; 194 if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) || 195 (m == STOPPED_ON_SOMETHING) || 196 interrupted) { 197 break; 198 } 199 } while (!next_to_something(row, col)); 200 if ( (!interrupted) && passgo && (m == MOVE_FAILED) && 201 (dungeon[rogue.row][rogue.col] & TUNNEL)) { 202 turn_passage(dirch + 96, 0); 203 } 204 break; 205 case 'H': 206 case 'J': 207 case 'K': 208 case 'L': 209 case 'B': 210 case 'Y': 211 case 'U': 212 case 'N': 213 while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED)) 214 ; 215 216 if ( (!interrupted) && passgo && 217 (dungeon[rogue.row][rogue.col] & TUNNEL)) { 218 turn_passage(dirch + 32, 1); 219 } 220 break; 221 } 222 } 223 224 boolean 225 is_passable(int row, int col) 226 { 227 if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) || 228 (col > (DCOLS-1))) { 229 return(0); 230 } 231 if (dungeon[row][col] & HIDDEN) { 232 return((dungeon[row][col] & TRAP) ? 1 : 0); 233 } 234 return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP)); 235 } 236 237 static boolean 238 next_to_something(int drow, int dcol) 239 { 240 short i, j, i_end, j_end, row, col; 241 short pass_count = 0; 242 unsigned short s; 243 244 if (confused) { 245 return(1); 246 } 247 if (blind) { 248 return(0); 249 } 250 i_end = (rogue.row < (DROWS-2)) ? 1 : 0; 251 j_end = (rogue.col < (DCOLS-1)) ? 1 : 0; 252 253 for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { 254 for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) { 255 if ((i == 0) && (j == 0)) { 256 continue; 257 } 258 if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) { 259 continue; 260 } 261 row = rogue.row + i; 262 col = rogue.col + j; 263 s = dungeon[row][col]; 264 if (s & HIDDEN) { 265 continue; 266 } 267 /* If the rogue used to be right, up, left, down, or right of 268 * row,col, and now isn't, then don't stop */ 269 if (s & (MONSTER | OBJECT | STAIRS)) { 270 if (((row == drow) || (col == dcol)) && 271 (!((row == rogue.row) || (col == rogue.col)))) { 272 continue; 273 } 274 return(1); 275 } 276 if (s & TRAP) { 277 if (!(s & HIDDEN)) { 278 if (((row == drow) || (col == dcol)) && 279 (!((row == rogue.row) || (col == rogue.col)))) { 280 continue; 281 } 282 return(1); 283 } 284 } 285 if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) { 286 if (++pass_count > 1) { 287 return(1); 288 } 289 } 290 if ((s & DOOR) && ((i == 0) || (j == 0))) { 291 return(1); 292 } 293 } 294 } 295 return(0); 296 } 297 298 boolean 299 can_move(short row1, short col1, short row2, short col2) 300 { 301 if (!is_passable(row2, col2)) { 302 return(0); 303 } 304 if ((row1 != row2) && (col1 != col2)) { 305 if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) { 306 return(0); 307 } 308 if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) { 309 return(0); 310 } 311 } 312 return(1); 313 } 314 315 void 316 move_onto(void) 317 { 318 short ch, d; 319 boolean first_miss = 1; 320 321 while (!is_direction(ch = rgetchar(), &d)) { 322 sound_bell(); 323 if (first_miss) { 324 message("direction? ", 0); 325 first_miss = 0; 326 } 327 } 328 check_message(); 329 if (ch != CANCEL) { 330 one_move_rogue(ch, 0); 331 } 332 } 333 334 boolean 335 is_direction(short c, short *d) 336 { 337 switch(c) { 338 case 'h': 339 *d = LEFT; 340 break; 341 case 'j': 342 *d = DOWN; 343 break; 344 case 'k': 345 *d = UPWARD; 346 break; 347 case 'l': 348 *d = RIGHT; 349 break; 350 case 'b': 351 *d = DOWNLEFT; 352 break; 353 case 'y': 354 *d = UPLEFT; 355 break; 356 case 'u': 357 *d = UPRIGHT; 358 break; 359 case 'n': 360 *d = DOWNRIGHT; 361 break; 362 case CANCEL: 363 break; 364 default: 365 return(0); 366 } 367 return(1); 368 } 369 370 static boolean 371 check_hunger(boolean msg_only) 372 { 373 short i, n; 374 boolean fainted = 0; 375 376 if (rogue.moves_left == HUNGRY) { 377 strcpy(hunger_str, "hungry"); 378 message(hunger_str, 0); 379 print_stats(STAT_HUNGER); 380 } 381 if (rogue.moves_left == WEAK) { 382 strcpy(hunger_str, "weak"); 383 message(hunger_str, 1); 384 print_stats(STAT_HUNGER); 385 } 386 if (rogue.moves_left <= FAINT) { 387 if (rogue.moves_left == FAINT) { 388 strcpy(hunger_str, "faint"); 389 message(hunger_str, 1); 390 print_stats(STAT_HUNGER); 391 } 392 n = get_rand(0, (FAINT - rogue.moves_left)); 393 if (n > 0) { 394 fainted = 1; 395 if (rand_percent(40)) { 396 rogue.moves_left++; 397 } 398 message("you faint", 1); 399 for (i = 0; i < n; i++) { 400 if (coin_toss()) { 401 mv_mons(); 402 } 403 } 404 message(you_can_move_again, 1); 405 } 406 } 407 if (msg_only) { 408 return(fainted); 409 } 410 if (rogue.moves_left <= STARVE) { 411 killed_by(NULL, STARVATION); 412 } 413 414 switch(e_rings) { 415 case -1: 416 rogue.moves_left -= (rogue.moves_left % 2); 417 break; 418 case 0: 419 rogue.moves_left--; 420 break; 421 case 1: 422 rogue.moves_left--; 423 check_hunger(1); 424 rogue.moves_left -= (rogue.moves_left % 2); 425 break; 426 case 2: 427 rogue.moves_left--; 428 check_hunger(1); 429 rogue.moves_left--; 430 break; 431 } 432 return(fainted); 433 } 434 435 boolean 436 reg_move(void) 437 { 438 boolean fainted; 439 440 if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) { 441 fainted = check_hunger(0); 442 } else { 443 fainted = 0; 444 } 445 446 mv_mons(); 447 448 if (++m_moves >= 120) { 449 m_moves = 0; 450 wanderer(); 451 } 452 if (halluc) { 453 if (!(--halluc)) { 454 unhallucinate(); 455 } else { 456 hallucinate(); 457 } 458 } 459 if (blind) { 460 if (!(--blind)) { 461 unblind(); 462 } 463 } 464 if (confused) { 465 if (!(--confused)) { 466 unconfuse(); 467 } 468 } 469 if (bear_trap) { 470 bear_trap--; 471 } 472 if (levitate) { 473 if (!(--levitate)) { 474 message("you float gently to the ground", 1); 475 if (dungeon[rogue.row][rogue.col] & TRAP) { 476 trap_player(rogue.row, rogue.col); 477 } 478 } 479 } 480 if (haste_self) { 481 if (!(--haste_self)) { 482 message("you feel yourself slowing down", 0); 483 } 484 } 485 heal(); 486 if (auto_search > 0) { 487 search(auto_search, auto_search); 488 } 489 return(fainted); 490 } 491 492 void 493 rest(int count) 494 { 495 int i; 496 497 interrupted = 0; 498 499 for (i = 0; i < count; i++) { 500 if (interrupted) { 501 break; 502 } 503 reg_move(); 504 } 505 } 506 507 static short 508 gr_dir(void) 509 { 510 short d; 511 512 d = get_rand(1, 8); 513 514 switch(d) { 515 case 1: 516 d = 'j'; 517 break; 518 case 2: 519 d = 'k'; 520 break; 521 case 3: 522 d = 'l'; 523 break; 524 case 4: 525 d = 'h'; 526 break; 527 case 5: 528 d = 'y'; 529 break; 530 case 6: 531 d = 'u'; 532 break; 533 case 7: 534 d = 'b'; 535 break; 536 case 8: 537 d = 'n'; 538 break; 539 } 540 return(d); 541 } 542 543 static void 544 heal(void) 545 { 546 static short heal_exp = -1, n, c = 0; 547 static boolean alt; 548 549 if (rogue.hp_current == rogue.hp_max) { 550 c = 0; 551 return; 552 } 553 if (rogue.exp != heal_exp) { 554 heal_exp = rogue.exp; 555 556 switch(heal_exp) { 557 case 1: 558 n = 20; 559 break; 560 case 2: 561 n = 18; 562 break; 563 case 3: 564 n = 17; 565 break; 566 case 4: 567 n = 14; 568 break; 569 case 5: 570 n = 13; 571 break; 572 case 6: 573 n = 10; 574 break; 575 case 7: 576 n = 9; 577 break; 578 case 8: 579 n = 8; 580 break; 581 case 9: 582 n = 7; 583 break; 584 case 10: 585 n = 4; 586 break; 587 case 11: 588 n = 3; 589 break; 590 case 12: 591 default: 592 n = 2; 593 } 594 } 595 if (++c >= n) { 596 c = 0; 597 rogue.hp_current++; 598 if ((alt = !alt) != 0) { 599 rogue.hp_current++; 600 } 601 if ((rogue.hp_current += regeneration) > rogue.hp_max) { 602 rogue.hp_current = rogue.hp_max; 603 } 604 print_stats(STAT_HP); 605 } 606 } 607 608 static boolean 609 can_turn(short nrow, short ncol) 610 { 611 if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) { 612 return(1); 613 } 614 return(0); 615 } 616 617 static void 618 turn_passage(short dir, boolean fast) 619 { 620 short crow = rogue.row, ccol = rogue.col, turns = 0; 621 short ndir = 0; 622 623 if ((dir != 'h') && can_turn(crow, ccol + 1)) { 624 turns++; 625 ndir = 'l'; 626 } 627 if ((dir != 'l') && can_turn(crow, ccol - 1)) { 628 turns++; 629 ndir = 'h'; 630 } 631 if ((dir != 'k') && can_turn(crow + 1, ccol)) { 632 turns++; 633 ndir = 'j'; 634 } 635 if ((dir != 'j') && can_turn(crow - 1, ccol)) { 636 turns++; 637 ndir = 'k'; 638 } 639 if (turns == 1) { 640 multiple_move_rogue(ndir - (fast ? 32 : 96)); 641 } 642 } 643