1 /* $NetBSD: move.c,v 1.11 2008/01/14 03:50:02 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[] = "@(#)move.c 8.1 (Berkeley) 5/31/93"; 39 #else 40 __RCSID("$NetBSD: move.c,v 1.11 2008/01/14 03:50:02 dholland Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 /* 45 * move.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 short m_moves = 0; 59 boolean jump = 0; 60 const char *you_can_move_again = "you can move again"; 61 62 int 63 one_move_rogue(short dirch, short pickup) 64 { 65 short row, col; 66 object *obj; 67 char desc[DCOLS]; 68 short status, d = 0; /* XXX: GCC */ 69 70 row = rogue.row; 71 col = rogue.col; 72 73 if (confused) { 74 dirch = gr_dir(); 75 } 76 (void)is_direction(dirch, &d); 77 get_dir_rc(d, &row, &col, 1); 78 79 if (!can_move(rogue.row, rogue.col, row, col)) { 80 return(MOVE_FAILED); 81 } 82 if (being_held || bear_trap) { 83 if (!(dungeon[row][col] & MONSTER)) { 84 if (being_held) { 85 messagef(1, "you are being held"); 86 } else { 87 messagef(0, "you are still stuck in the bear trap"); 88 (void)reg_move(); 89 } 90 return(MOVE_FAILED); 91 } 92 } 93 if (r_teleport) { 94 if (rand_percent(R_TELE_PERCENT)) { 95 tele(); 96 return(STOPPED_ON_SOMETHING); 97 } 98 } 99 if (dungeon[row][col] & MONSTER) { 100 rogue_hit(object_at(&level_monsters, row, col), 0); 101 (void)reg_move(); 102 return(MOVE_FAILED); 103 } 104 if (dungeon[row][col] & DOOR) { 105 if (cur_room == PASSAGE) { 106 cur_room = get_room_number(row, col); 107 if (cur_room == NO_ROOM) 108 clean_up("one_move_rogue: door to nowhere"); 109 light_up_room(cur_room); 110 wake_room(cur_room, 1, row, col); 111 } else { 112 light_passage(row, col); 113 } 114 } else if ((dungeon[rogue.row][rogue.col] & DOOR) && 115 (dungeon[row][col] & TUNNEL)) { 116 light_passage(row, col); 117 wake_room(cur_room, 0, rogue.row, rogue.col); 118 darken_room(cur_room); 119 cur_room = PASSAGE; 120 } else if (dungeon[row][col] & TUNNEL) { 121 light_passage(row, col); 122 } 123 mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col)); 124 mvaddch(row, col, rogue.fchar); 125 126 if (!jump) { 127 refresh(); 128 } 129 rogue.row = row; 130 rogue.col = col; 131 if (dungeon[row][col] & OBJECT) { 132 if (levitate && pickup) { 133 return(STOPPED_ON_SOMETHING); 134 } 135 if (pickup && !levitate) { 136 if ((obj = pick_up(row, col, &status)) != NULL) { 137 get_desc(obj, desc, sizeof(desc)); 138 if (obj->what_is == GOLD) { 139 free_object(obj); 140 messagef(1, "%s", desc); 141 goto NOT_IN_PACK; 142 } 143 } else if (!status) { 144 goto MVED; 145 } else { 146 goto MOVE_ON; 147 } 148 } else { 149 MOVE_ON: 150 obj = object_at(&level_objects, row, col); 151 get_desc(obj, desc, sizeof(desc)); 152 messagef(1, "moved onto %s", desc); 153 goto NOT_IN_PACK; 154 } 155 messagef(1, "%s(%c)", desc, obj->ichar); 156 NOT_IN_PACK: 157 (void)reg_move(); 158 return(STOPPED_ON_SOMETHING); 159 } 160 if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) { 161 if ((!levitate) && (dungeon[row][col] & TRAP)) { 162 trap_player(row, col); 163 } 164 (void)reg_move(); 165 return(STOPPED_ON_SOMETHING); 166 } 167 MVED: if (reg_move()) { /* fainted from hunger */ 168 return(STOPPED_ON_SOMETHING); 169 } 170 return((confused ? STOPPED_ON_SOMETHING : MOVED)); 171 } 172 173 void 174 multiple_move_rogue(short dirch) 175 { 176 short row, col; 177 short m; 178 179 switch(dirch) { 180 case '\010': 181 case '\012': 182 case '\013': 183 case '\014': 184 case '\031': 185 case '\025': 186 case '\016': 187 case '\002': 188 do { 189 row = rogue.row; 190 col = rogue.col; 191 if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) || 192 (m == STOPPED_ON_SOMETHING) || 193 interrupted) { 194 break; 195 } 196 } while (!next_to_something(row, col)); 197 if ( (!interrupted) && passgo && (m == MOVE_FAILED) && 198 (dungeon[rogue.row][rogue.col] & TUNNEL)) { 199 turn_passage(dirch + 96, 0); 200 } 201 break; 202 case 'H': 203 case 'J': 204 case 'K': 205 case 'L': 206 case 'B': 207 case 'Y': 208 case 'U': 209 case 'N': 210 while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED)) 211 ; 212 213 if ( (!interrupted) && passgo && 214 (dungeon[rogue.row][rogue.col] & TUNNEL)) { 215 turn_passage(dirch + 32, 1); 216 } 217 break; 218 } 219 } 220 221 boolean 222 is_passable(int row, int col) 223 { 224 if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) || 225 (col > (DCOLS-1))) { 226 return(0); 227 } 228 if (dungeon[row][col] & HIDDEN) { 229 return((dungeon[row][col] & TRAP) ? 1 : 0); 230 } 231 return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP)); 232 } 233 234 boolean 235 next_to_something(int drow, int dcol) 236 { 237 short i, j, i_end, j_end, row, col; 238 short pass_count = 0; 239 unsigned short s; 240 241 if (confused) { 242 return(1); 243 } 244 if (blind) { 245 return(0); 246 } 247 i_end = (rogue.row < (DROWS-2)) ? 1 : 0; 248 j_end = (rogue.col < (DCOLS-1)) ? 1 : 0; 249 250 for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { 251 for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) { 252 if ((i == 0) && (j == 0)) { 253 continue; 254 } 255 if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) { 256 continue; 257 } 258 row = rogue.row + i; 259 col = rogue.col + j; 260 s = dungeon[row][col]; 261 if (s & HIDDEN) { 262 continue; 263 } 264 /* If the rogue used to be right, up, left, down, or right of 265 * row,col, and now isn't, then don't stop */ 266 if (s & (MONSTER | OBJECT | STAIRS)) { 267 if (((row == drow) || (col == dcol)) && 268 (!((row == rogue.row) || (col == rogue.col)))) { 269 continue; 270 } 271 return(1); 272 } 273 if (s & TRAP) { 274 if (!(s & HIDDEN)) { 275 if (((row == drow) || (col == dcol)) && 276 (!((row == rogue.row) || (col == rogue.col)))) { 277 continue; 278 } 279 return(1); 280 } 281 } 282 if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) { 283 if (++pass_count > 1) { 284 return(1); 285 } 286 } 287 if ((s & DOOR) && ((i == 0) || (j == 0))) { 288 return(1); 289 } 290 } 291 } 292 return(0); 293 } 294 295 boolean 296 can_move(int row1, int col1, int row2, int col2) 297 { 298 if (!is_passable(row2, col2)) { 299 return(0); 300 } 301 if ((row1 != row2) && (col1 != col2)) { 302 if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) { 303 return(0); 304 } 305 if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) { 306 return(0); 307 } 308 } 309 return(1); 310 } 311 312 void 313 move_onto(void) 314 { 315 short ch, d; 316 boolean first_miss = 1; 317 318 while (!is_direction(ch = rgetchar(), &d)) { 319 sound_bell(); 320 if (first_miss) { 321 messagef(0, "direction? "); 322 first_miss = 0; 323 } 324 } 325 check_message(); 326 if (ch != CANCEL) { 327 (void)one_move_rogue(ch, 0); 328 } 329 } 330 331 boolean 332 is_direction(short c, short *d) 333 { 334 switch(c) { 335 case 'h': 336 *d = LEFT; 337 break; 338 case 'j': 339 *d = DOWN; 340 break; 341 case 'k': 342 *d = UPWARD; 343 break; 344 case 'l': 345 *d = RIGHT; 346 break; 347 case 'b': 348 *d = DOWNLEFT; 349 break; 350 case 'y': 351 *d = UPLEFT; 352 break; 353 case 'u': 354 *d = UPRIGHT; 355 break; 356 case 'n': 357 *d = DOWNRIGHT; 358 break; 359 case CANCEL: 360 break; 361 default: 362 return(0); 363 } 364 return(1); 365 } 366 367 boolean 368 check_hunger(boolean msg_only) 369 { 370 short i, n; 371 boolean fainted = 0; 372 373 if (rogue.moves_left == HUNGRY) { 374 (void)strlcpy(hunger_str, "hungry", sizeof(hunger_str)); 375 messagef(0, "%s", hunger_str); 376 print_stats(STAT_HUNGER); 377 } 378 if (rogue.moves_left == WEAK) { 379 (void)strlcpy(hunger_str, "weak", sizeof(hunger_str)); 380 messagef(1, "%s", hunger_str); 381 print_stats(STAT_HUNGER); 382 } 383 if (rogue.moves_left <= FAINT) { 384 if (rogue.moves_left == FAINT) { 385 (void)strlcpy(hunger_str, "faint", sizeof(hunger_str)); 386 messagef(1, "%s", hunger_str); 387 print_stats(STAT_HUNGER); 388 } 389 n = get_rand(0, (FAINT - rogue.moves_left)); 390 if (n > 0) { 391 fainted = 1; 392 if (rand_percent(40)) { 393 rogue.moves_left++; 394 } 395 messagef(1, "you faint"); 396 for (i = 0; i < n; i++) { 397 if (coin_toss()) { 398 mv_mons(); 399 } 400 } 401 messagef(1, you_can_move_again); 402 } 403 } 404 if (msg_only) { 405 return(fainted); 406 } 407 if (rogue.moves_left <= STARVE) { 408 killed_by(NULL, STARVATION); 409 } 410 411 switch(e_rings) { 412 /*case -2: 413 Subtract 0, i.e. do nothing. 414 break;*/ 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 (void)check_hunger(1); 424 rogue.moves_left -= (rogue.moves_left % 2); 425 break; 426 case 2: 427 rogue.moves_left--; 428 (void)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 messagef(1, "you float gently to the ground"); 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 messagef(0, "you feel yourself slowing down"); 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 (void)reg_move(); 504 } 505 } 506 507 char 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 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 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 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