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