650 lines
13 KiB
C
650 lines
13 KiB
C
/* $NetBSD: move.c,v 1.12 2009/08/12 08:44:45 dholland Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1988, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Timothy C. Stoehr.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)move.c 8.1 (Berkeley) 5/31/93";
|
|
#else
|
|
__RCSID("$NetBSD: move.c,v 1.12 2009/08/12 08:44:45 dholland Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
/*
|
|
* move.c
|
|
*
|
|
* This source herein may be modified and/or distributed by anybody who
|
|
* so desires, with the following restrictions:
|
|
* 1.) No portion of this notice shall be removed.
|
|
* 2.) Credit shall not be taken for the creation of this source.
|
|
* 3.) This code is not to be traded, sold, or used for personal
|
|
* gain or profit.
|
|
*
|
|
*/
|
|
|
|
#include "rogue.h"
|
|
|
|
short m_moves = 0;
|
|
boolean jump = 0;
|
|
const char *you_can_move_again = "you can move again";
|
|
|
|
static boolean can_turn(short, short);
|
|
static boolean check_hunger(boolean);
|
|
static char gr_dir(void);
|
|
static void heal(void);
|
|
static boolean next_to_something(int, int);
|
|
static void turn_passage(short, boolean);
|
|
|
|
int
|
|
one_move_rogue(short dirch, short pickup)
|
|
{
|
|
short row, col;
|
|
object *obj;
|
|
char desc[DCOLS];
|
|
short status, d = 0; /* XXX: GCC */
|
|
|
|
row = rogue.row;
|
|
col = rogue.col;
|
|
|
|
if (confused) {
|
|
dirch = gr_dir();
|
|
}
|
|
(void)is_direction(dirch, &d);
|
|
get_dir_rc(d, &row, &col, 1);
|
|
|
|
if (!can_move(rogue.row, rogue.col, row, col)) {
|
|
return(MOVE_FAILED);
|
|
}
|
|
if (being_held || bear_trap) {
|
|
if (!(dungeon[row][col] & MONSTER)) {
|
|
if (being_held) {
|
|
messagef(1, "you are being held");
|
|
} else {
|
|
messagef(0, "you are still stuck in the bear trap");
|
|
(void)reg_move();
|
|
}
|
|
return(MOVE_FAILED);
|
|
}
|
|
}
|
|
if (r_teleport) {
|
|
if (rand_percent(R_TELE_PERCENT)) {
|
|
tele();
|
|
return(STOPPED_ON_SOMETHING);
|
|
}
|
|
}
|
|
if (dungeon[row][col] & MONSTER) {
|
|
rogue_hit(object_at(&level_monsters, row, col), 0);
|
|
(void)reg_move();
|
|
return(MOVE_FAILED);
|
|
}
|
|
if (dungeon[row][col] & DOOR) {
|
|
if (cur_room == PASSAGE) {
|
|
cur_room = get_room_number(row, col);
|
|
if (cur_room == NO_ROOM)
|
|
clean_up("one_move_rogue: door to nowhere");
|
|
light_up_room(cur_room);
|
|
wake_room(cur_room, 1, row, col);
|
|
} else {
|
|
light_passage(row, col);
|
|
}
|
|
} else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
|
|
(dungeon[row][col] & TUNNEL)) {
|
|
light_passage(row, col);
|
|
wake_room(cur_room, 0, rogue.row, rogue.col);
|
|
darken_room(cur_room);
|
|
cur_room = PASSAGE;
|
|
} else if (dungeon[row][col] & TUNNEL) {
|
|
light_passage(row, col);
|
|
}
|
|
mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
|
|
mvaddch(row, col, rogue.fchar);
|
|
|
|
if (!jump) {
|
|
refresh();
|
|
}
|
|
rogue.row = row;
|
|
rogue.col = col;
|
|
if (dungeon[row][col] & OBJECT) {
|
|
if (levitate && pickup) {
|
|
return(STOPPED_ON_SOMETHING);
|
|
}
|
|
if (pickup && !levitate) {
|
|
if ((obj = pick_up(row, col, &status)) != NULL) {
|
|
get_desc(obj, desc, sizeof(desc));
|
|
if (obj->what_is == GOLD) {
|
|
free_object(obj);
|
|
messagef(1, "%s", desc);
|
|
goto NOT_IN_PACK;
|
|
}
|
|
} else if (!status) {
|
|
goto MVED;
|
|
} else {
|
|
goto MOVE_ON;
|
|
}
|
|
} else {
|
|
MOVE_ON:
|
|
obj = object_at(&level_objects, row, col);
|
|
get_desc(obj, desc, sizeof(desc));
|
|
messagef(1, "moved onto %s", desc);
|
|
goto NOT_IN_PACK;
|
|
}
|
|
messagef(1, "%s(%c)", desc, obj->ichar);
|
|
NOT_IN_PACK:
|
|
(void)reg_move();
|
|
return(STOPPED_ON_SOMETHING);
|
|
}
|
|
if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
|
|
if ((!levitate) && (dungeon[row][col] & TRAP)) {
|
|
trap_player(row, col);
|
|
}
|
|
(void)reg_move();
|
|
return(STOPPED_ON_SOMETHING);
|
|
}
|
|
MVED: if (reg_move()) { /* fainted from hunger */
|
|
return(STOPPED_ON_SOMETHING);
|
|
}
|
|
return((confused ? STOPPED_ON_SOMETHING : MOVED));
|
|
}
|
|
|
|
void
|
|
multiple_move_rogue(short dirch)
|
|
{
|
|
short row, col;
|
|
short m;
|
|
|
|
switch(dirch) {
|
|
case '\010':
|
|
case '\012':
|
|
case '\013':
|
|
case '\014':
|
|
case '\031':
|
|
case '\025':
|
|
case '\016':
|
|
case '\002':
|
|
do {
|
|
row = rogue.row;
|
|
col = rogue.col;
|
|
if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
|
|
(m == STOPPED_ON_SOMETHING) ||
|
|
interrupted) {
|
|
break;
|
|
}
|
|
} while (!next_to_something(row, col));
|
|
if ( (!interrupted) && passgo && (m == MOVE_FAILED) &&
|
|
(dungeon[rogue.row][rogue.col] & TUNNEL)) {
|
|
turn_passage(dirch + 96, 0);
|
|
}
|
|
break;
|
|
case 'H':
|
|
case 'J':
|
|
case 'K':
|
|
case 'L':
|
|
case 'B':
|
|
case 'Y':
|
|
case 'U':
|
|
case 'N':
|
|
while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED))
|
|
;
|
|
|
|
if ( (!interrupted) && passgo &&
|
|
(dungeon[rogue.row][rogue.col] & TUNNEL)) {
|
|
turn_passage(dirch + 32, 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
boolean
|
|
is_passable(int row, int col)
|
|
{
|
|
if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
|
|
(col > (DCOLS-1))) {
|
|
return(0);
|
|
}
|
|
if (dungeon[row][col] & HIDDEN) {
|
|
return((dungeon[row][col] & TRAP) ? 1 : 0);
|
|
}
|
|
return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
|
|
}
|
|
|
|
static boolean
|
|
next_to_something(int drow, int dcol)
|
|
{
|
|
short i, j, i_end, j_end, row, col;
|
|
short pass_count = 0;
|
|
unsigned short s;
|
|
|
|
if (confused) {
|
|
return(1);
|
|
}
|
|
if (blind) {
|
|
return(0);
|
|
}
|
|
i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
|
|
j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
|
|
|
|
for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
|
|
for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
|
|
if ((i == 0) && (j == 0)) {
|
|
continue;
|
|
}
|
|
if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
|
|
continue;
|
|
}
|
|
row = rogue.row + i;
|
|
col = rogue.col + j;
|
|
s = dungeon[row][col];
|
|
if (s & HIDDEN) {
|
|
continue;
|
|
}
|
|
/* If the rogue used to be right, up, left, down, or right of
|
|
* row,col, and now isn't, then don't stop */
|
|
if (s & (MONSTER | OBJECT | STAIRS)) {
|
|
if (((row == drow) || (col == dcol)) &&
|
|
(!((row == rogue.row) || (col == rogue.col)))) {
|
|
continue;
|
|
}
|
|
return(1);
|
|
}
|
|
if (s & TRAP) {
|
|
if (!(s & HIDDEN)) {
|
|
if (((row == drow) || (col == dcol)) &&
|
|
(!((row == rogue.row) || (col == rogue.col)))) {
|
|
continue;
|
|
}
|
|
return(1);
|
|
}
|
|
}
|
|
if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
|
|
if (++pass_count > 1) {
|
|
return(1);
|
|
}
|
|
}
|
|
if ((s & DOOR) && ((i == 0) || (j == 0))) {
|
|
return(1);
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
boolean
|
|
can_move(int row1, int col1, int row2, int col2)
|
|
{
|
|
if (!is_passable(row2, col2)) {
|
|
return(0);
|
|
}
|
|
if ((row1 != row2) && (col1 != col2)) {
|
|
if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
|
|
return(0);
|
|
}
|
|
if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
|
|
return(0);
|
|
}
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
void
|
|
move_onto(void)
|
|
{
|
|
short ch, d;
|
|
boolean first_miss = 1;
|
|
|
|
while (!is_direction(ch = rgetchar(), &d)) {
|
|
sound_bell();
|
|
if (first_miss) {
|
|
messagef(0, "direction? ");
|
|
first_miss = 0;
|
|
}
|
|
}
|
|
check_message();
|
|
if (ch != CANCEL) {
|
|
(void)one_move_rogue(ch, 0);
|
|
}
|
|
}
|
|
|
|
boolean
|
|
is_direction(short c, short *d)
|
|
{
|
|
switch(c) {
|
|
case 'h':
|
|
*d = LEFT;
|
|
break;
|
|
case 'j':
|
|
*d = DOWN;
|
|
break;
|
|
case 'k':
|
|
*d = UPWARD;
|
|
break;
|
|
case 'l':
|
|
*d = RIGHT;
|
|
break;
|
|
case 'b':
|
|
*d = DOWNLEFT;
|
|
break;
|
|
case 'y':
|
|
*d = UPLEFT;
|
|
break;
|
|
case 'u':
|
|
*d = UPRIGHT;
|
|
break;
|
|
case 'n':
|
|
*d = DOWNRIGHT;
|
|
break;
|
|
case CANCEL:
|
|
break;
|
|
default:
|
|
return(0);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
static boolean
|
|
check_hunger(boolean msg_only)
|
|
{
|
|
short i, n;
|
|
boolean fainted = 0;
|
|
|
|
if (rogue.moves_left == HUNGRY) {
|
|
(void)strlcpy(hunger_str, "hungry", sizeof(hunger_str));
|
|
messagef(0, "%s", hunger_str);
|
|
print_stats(STAT_HUNGER);
|
|
}
|
|
if (rogue.moves_left == WEAK) {
|
|
(void)strlcpy(hunger_str, "weak", sizeof(hunger_str));
|
|
messagef(1, "%s", hunger_str);
|
|
print_stats(STAT_HUNGER);
|
|
}
|
|
if (rogue.moves_left <= FAINT) {
|
|
if (rogue.moves_left == FAINT) {
|
|
(void)strlcpy(hunger_str, "faint", sizeof(hunger_str));
|
|
messagef(1, "%s", hunger_str);
|
|
print_stats(STAT_HUNGER);
|
|
}
|
|
n = get_rand(0, (FAINT - rogue.moves_left));
|
|
if (n > 0) {
|
|
fainted = 1;
|
|
if (rand_percent(40)) {
|
|
rogue.moves_left++;
|
|
}
|
|
messagef(1, "you faint");
|
|
for (i = 0; i < n; i++) {
|
|
if (coin_toss()) {
|
|
mv_mons();
|
|
}
|
|
}
|
|
messagef(1, you_can_move_again);
|
|
}
|
|
}
|
|
if (msg_only) {
|
|
return(fainted);
|
|
}
|
|
if (rogue.moves_left <= STARVE) {
|
|
killed_by(NULL, STARVATION);
|
|
}
|
|
|
|
switch(e_rings) {
|
|
/*case -2:
|
|
Subtract 0, i.e. do nothing.
|
|
break;*/
|
|
case -1:
|
|
rogue.moves_left -= (rogue.moves_left % 2);
|
|
break;
|
|
case 0:
|
|
rogue.moves_left--;
|
|
break;
|
|
case 1:
|
|
rogue.moves_left--;
|
|
(void)check_hunger(1);
|
|
rogue.moves_left -= (rogue.moves_left % 2);
|
|
break;
|
|
case 2:
|
|
rogue.moves_left--;
|
|
(void)check_hunger(1);
|
|
rogue.moves_left--;
|
|
break;
|
|
}
|
|
return(fainted);
|
|
}
|
|
|
|
boolean
|
|
reg_move(void)
|
|
{
|
|
boolean fainted;
|
|
|
|
if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
|
|
fainted = check_hunger(0);
|
|
} else {
|
|
fainted = 0;
|
|
}
|
|
|
|
mv_mons();
|
|
|
|
if (++m_moves >= 120) {
|
|
m_moves = 0;
|
|
wanderer();
|
|
}
|
|
if (halluc) {
|
|
if (!(--halluc)) {
|
|
unhallucinate();
|
|
} else {
|
|
hallucinate();
|
|
}
|
|
}
|
|
if (blind) {
|
|
if (!(--blind)) {
|
|
unblind();
|
|
}
|
|
}
|
|
if (confused) {
|
|
if (!(--confused)) {
|
|
unconfuse();
|
|
}
|
|
}
|
|
if (bear_trap) {
|
|
bear_trap--;
|
|
}
|
|
if (levitate) {
|
|
if (!(--levitate)) {
|
|
messagef(1, "you float gently to the ground");
|
|
if (dungeon[rogue.row][rogue.col] & TRAP) {
|
|
trap_player(rogue.row, rogue.col);
|
|
}
|
|
}
|
|
}
|
|
if (haste_self) {
|
|
if (!(--haste_self)) {
|
|
messagef(0, "you feel yourself slowing down");
|
|
}
|
|
}
|
|
heal();
|
|
if (auto_search > 0) {
|
|
search(auto_search, auto_search);
|
|
}
|
|
return(fainted);
|
|
}
|
|
|
|
void
|
|
rest(int count)
|
|
{
|
|
int i;
|
|
|
|
interrupted = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (interrupted) {
|
|
break;
|
|
}
|
|
(void)reg_move();
|
|
}
|
|
}
|
|
|
|
static char
|
|
gr_dir(void)
|
|
{
|
|
short d;
|
|
|
|
d = get_rand(1, 8);
|
|
|
|
switch(d) {
|
|
case 1:
|
|
d = 'j';
|
|
break;
|
|
case 2:
|
|
d = 'k';
|
|
break;
|
|
case 3:
|
|
d = 'l';
|
|
break;
|
|
case 4:
|
|
d = 'h';
|
|
break;
|
|
case 5:
|
|
d = 'y';
|
|
break;
|
|
case 6:
|
|
d = 'u';
|
|
break;
|
|
case 7:
|
|
d = 'b';
|
|
break;
|
|
case 8:
|
|
d = 'n';
|
|
break;
|
|
}
|
|
return(d);
|
|
}
|
|
|
|
static void
|
|
heal(void)
|
|
{
|
|
static short heal_exp = -1, n, c = 0;
|
|
static boolean alt;
|
|
|
|
if (rogue.hp_current == rogue.hp_max) {
|
|
c = 0;
|
|
return;
|
|
}
|
|
if (rogue.exp != heal_exp) {
|
|
heal_exp = rogue.exp;
|
|
|
|
switch(heal_exp) {
|
|
case 1:
|
|
n = 20;
|
|
break;
|
|
case 2:
|
|
n = 18;
|
|
break;
|
|
case 3:
|
|
n = 17;
|
|
break;
|
|
case 4:
|
|
n = 14;
|
|
break;
|
|
case 5:
|
|
n = 13;
|
|
break;
|
|
case 6:
|
|
n = 10;
|
|
break;
|
|
case 7:
|
|
n = 9;
|
|
break;
|
|
case 8:
|
|
n = 8;
|
|
break;
|
|
case 9:
|
|
n = 7;
|
|
break;
|
|
case 10:
|
|
n = 4;
|
|
break;
|
|
case 11:
|
|
n = 3;
|
|
break;
|
|
case 12:
|
|
default:
|
|
n = 2;
|
|
}
|
|
}
|
|
if (++c >= n) {
|
|
c = 0;
|
|
rogue.hp_current++;
|
|
if ((alt = !alt) != 0) {
|
|
rogue.hp_current++;
|
|
}
|
|
if ((rogue.hp_current += regeneration) > rogue.hp_max) {
|
|
rogue.hp_current = rogue.hp_max;
|
|
}
|
|
print_stats(STAT_HP);
|
|
}
|
|
}
|
|
|
|
static boolean
|
|
can_turn(short nrow, short ncol)
|
|
{
|
|
if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
turn_passage(short dir, boolean fast)
|
|
{
|
|
short crow = rogue.row, ccol = rogue.col, turns = 0;
|
|
short ndir = 0;
|
|
|
|
if ((dir != 'h') && can_turn(crow, ccol + 1)) {
|
|
turns++;
|
|
ndir = 'l';
|
|
}
|
|
if ((dir != 'l') && can_turn(crow, ccol - 1)) {
|
|
turns++;
|
|
ndir = 'h';
|
|
}
|
|
if ((dir != 'k') && can_turn(crow + 1, ccol)) {
|
|
turns++;
|
|
ndir = 'j';
|
|
}
|
|
if ((dir != 'j') && can_turn(crow - 1, ccol)) {
|
|
turns++;
|
|
ndir = 'k';
|
|
}
|
|
if (turns == 1) {
|
|
multiple_move_rogue(ndir - (fast ? 32 : 96));
|
|
}
|
|
}
|