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