Home | History | Annotate | Line # | Download | only in rogue
move.c revision 1.11
      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