remove prescient travel command behavior

Addresses reports R718, R772.1, <Someone> P's extra move bug
- when there is a previously seen path or a straight path, always take it
- incorporate fix to ensure no extra "." turn at the end of traveling, but
 still avoid stepping into traps/pools, et al
- include a general "G"-command (and travel) fix to avoid stepping in
 known pools/lava while blind
- when there is no such path, "guess" at a path by finding an intermediate
 location that the hero couldsee that is closest to the actual goal, the
 intermediate goal is re-determined at each step
- when Blind, don't use couldsee for determining straight paths, just direction
- do not consider doors or most boulders obstacles for picking travel
 paths, test_move has a new mode to differentiate this case from the regular
 test case
- don't include known trap locations in the travel path, avoids unnecessary
 stops along the way, and usually doesn't affect the path length
- reformatted the code a bit so I could follow it
This commit is contained in:
cohrs
2002-04-13 16:00:13 +00:00
parent a41aec240e
commit 1b321e92bc
7 changed files with 161 additions and 61 deletions

View File

@@ -476,7 +476,10 @@ Prefix: move until something interesting is found.
.lp "G[yuhjklbn] or <CONTROL->[yuhjklbn]
Prefix: same as `g', but forking of corridors is not considered interesting.
.lp _
Travel to a map location via a shortest-path algorithm. Stops on most of
Travel to a map location via a shortest-path algorithm. The shortest path
is computed over map locations the hero knows about (e.g. seen or
previously traversed). If there is no known path, a guess is made instead.
Stops on most of
the same conditions as the `G' command, but without picking up
objects, similar to the `M' command. For ports with mouse
support, the command is also invoked when a mouse-click takes place on a

View File

@@ -27,7 +27,7 @@
\begin{document}
%
% input file: guidebook.mn
% $Revision: 1.39 $ $Date: 2002/03/31 07:19:43 $
% $Revision: 1.40 $ $Date: 2002/04/04 03:44:18 $
%
%.ds h0 "
%.ds h1 %.ds h2 \%
@@ -638,7 +638,10 @@ Prefix: Same as `{\tt g}', but forking of corridors is not considered
interesting.
%.lp
\item[\tb{_}]
Travel to a map location via a shortest-path algorithm. Stops on most of
Travel to a map location via a shortest-path algorithm. The shortest path
is computed over map locations the hero knows about (e.g. seen or
previously traversed). If there is no known path, a guess is made instead.
Stops on most of
the same conditions as the `G' command, but without picking up
objects, similar to the `M' command. For ports with mouse
support, the command is also invoked when a mouse-click takes place on a

View File

@@ -67,6 +67,8 @@ randomize starting position on goal level for M, P, and S quests
prevent the Wizard of Yendor from displacing the high priest of Moloch out of
the Sanctum's temple
ATR_BOLD on spell menu header
travel command should restrict its shortest paths to areas of the map the
hero knows about or might reasonably guess
Platform- and/or Interface-Specific Fixes

View File

@@ -662,7 +662,7 @@ E boolean FDECL(may_dig, (XCHAR_P,XCHAR_P));
E boolean FDECL(may_passwall, (XCHAR_P,XCHAR_P));
E boolean FDECL(bad_rock, (struct permonst *,XCHAR_P,XCHAR_P));
E boolean FDECL(invocation_pos, (XCHAR_P,XCHAR_P));
E boolean FDECL(test_move, (int, int, int, int, BOOLEAN_P));
E boolean FDECL(test_move, (int, int, int, int, int));
E void NDECL(domove);
E void NDECL(invocation_message);
E void FDECL(spoteffects, (BOOLEAN_P));

View File

@@ -178,6 +178,11 @@ NEARDATA extern coord bhitpos; /* place where throw or zap hits or stops */
/* Flags to control dotrap() in trap.c */
#define NOWEBMSG 0x01 /* suppress stumble into web message */
/* Flags to control test_move in hack.c */
#define DO_MOVE 0 /* really doing the move */
#define TEST_MOVE 1 /* test a normal move (move there next) */
#define TEST_TRAV 2 /* test a future travel location */
/*** some utility macros ***/
#define yn(query) yn_function(query,ynchars, 'n')
#define ynq(query) yn_function(query,ynqchars, 'q')

View File

@@ -1977,7 +1977,7 @@ click_to_cmd(x, y, mod)
dir = xytod(x, y);
if (!m_at(u.ux+x, u.uy+y) && !test_move(u.ux, u.uy, x, y, 1)) {
if (!m_at(u.ux+x, u.uy+y) && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
cmd[1] = (iflags.num_pad ? ndir[dir] : sdir[dir]);
cmd[2] = 0;
if (IS_DOOR(levl[u.ux+x][u.uy+y].typ)) {

View File

@@ -12,7 +12,7 @@ STATIC_DCL int FDECL(still_chewing,(XCHAR_P,XCHAR_P));
#ifdef SINKS
STATIC_DCL void NDECL(dosinkfall);
#endif
STATIC_DCL void NDECL(findtravelpath);
STATIC_DCL boolean FDECL(findtravelpath, (BOOLEAN_P));
STATIC_DCL boolean FDECL(monstinroom, (struct permonst *,int));
STATIC_DCL void FDECL(move_update, (BOOLEAN_P));
@@ -526,22 +526,27 @@ xchar x, y;
#endif /* OVL1 */
#ifdef OVL3
/* return TRUE if (dx,dy) is an OK place to move */
/* return TRUE if (dx,dy) is an OK place to move
* mode is one of DO_MOVE, TEST_MOVE or TEST_TRAV
*/
boolean
test_move(ux, uy, dx, dy, test_only)
test_move(ux, uy, dx, dy, mode)
int ux, uy, dx, dy;
boolean test_only;
int mode;
{
int x = ux+dx;
int y = uy+dy;
register struct rm *tmpr = &levl[x][y];
register struct rm *ust;
/* if this is the next step, must treat as if TEST_MOVE were used */
if (mode == TEST_TRAV && distmin(x, y, u.ux, u.uy) <= 1) mode = TEST_MOVE;
/*
* Check for physical obstacles. First, the place we are going.
*/
if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
if (Blind && !test_only) feel_location(x,y);
if (Blind && mode == DO_MOVE) feel_location(x,y);
if (Passes_walls && may_passwall(x,y)) {
; /* do nothing */
} else if (tmpr->typ == IRONBARS) {
@@ -549,34 +554,34 @@ boolean test_only;
return FALSE;
} else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
/* Eat the rock. */
if (!test_only && still_chewing(x,y)) return FALSE;
if (mode == DO_MOVE && still_chewing(x,y)) return FALSE;
} else if (flags.autodig && !flags.run && !flags.nopick &&
uwep && is_pick(uwep)) {
/* MRKR: Automatic digging when wielding the appropriate tool */
if (!test_only)
if (mode == DO_MOVE)
(void) use_pick_axe2(uwep);
return FALSE;
} else {
if ( !test_only ) {
if (mode == DO_MOVE) {
if (Is_stronghold(&u.uz) && is_db_wall(x,y))
pline_The("drawbridge is up!");
if (Passes_walls && !may_passwall(x,y) && In_sokoban(&u.uz))
pline_The("Sokoban walls resist your ability.");
pline_The("Sokoban walls resist your ability.");
}
return FALSE;
}
} else if (IS_DOOR(tmpr->typ)) {
if (closed_door(x,y)) {
if (Blind && !test_only) feel_location(x,y);
if (Blind && mode == DO_MOVE) feel_location(x,y);
if (Passes_walls)
; /* do nothing */
else if (can_ooze(&youmonst)) {
if ( !test_only ) You("ooze under the door.");
if (mode == DO_MOVE) You("ooze under the door.");
} else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
/* Eat the door. */
if (!test_only && still_chewing(x,y)) return FALSE;
if (mode == DO_MOVE && still_chewing(x,y)) return FALSE;
} else {
if ( !test_only ) {
if (mode == DO_MOVE) {
if (amorphous(youmonst.data))
You("try to ooze under the door, but can't squeeze your possessions through.");
else if (x == ux || y == uy) {
@@ -596,7 +601,7 @@ boolean test_only;
} else pline("That door is closed.");
}
}
return FALSE;
if (mode != TEST_TRAV) return FALSE;
}
} else if (dx && dy && !Passes_walls
&& ((tmpr->doormask & ~D_BROKEN)
@@ -605,7 +610,7 @@ boolean test_only;
#endif
|| block_door(x,y))) {
/* Diagonal moves into a door are not allowed. */
if ( Blind && !test_only )
if (Blind && mode == DO_MOVE)
feel_location(x,y);
return FALSE;
}
@@ -614,21 +619,27 @@ boolean test_only;
&& bad_rock(youmonst.data,ux,y) && bad_rock(youmonst.data,x,uy)) {
/* Move at a diagonal. */
if (In_sokoban(&u.uz)) {
if ( !test_only )
if (mode == DO_MOVE)
You("cannot pass that way.");
return FALSE;
}
if (bigmonst(youmonst.data)) {
if ( !test_only )
if (mode == DO_MOVE)
Your("body is too large to fit through.");
return FALSE;
}
if (invent && (inv_weight() + weight_cap() > 600)) {
if ( !test_only )
if (mode == DO_MOVE)
You("are carrying too much to get through.");
return FALSE;
}
}
/* pick a path that does not require crossing a trap */
if (flags.run == 8 && mode != DO_MOVE) {
struct trap* t = t_at(x, y);
if (t && t->tseen) return FALSE;
}
ust = &levl[ux][uy];
@@ -645,80 +656,152 @@ boolean test_only;
}
if (sobj_at(BOULDER,x,y) && (In_sokoban(&u.uz) || !Passes_walls)) {
if (!(Blind || Hallucination) && (flags.run >= 2))
if (!(Blind || Hallucination) && (flags.run >= 2) && mode != TEST_TRAV)
return FALSE;
if (!test_only) {
if (mode == DO_MOVE) {
/* tunneling monsters will chew before pushing */
if (tunnels(youmonst.data) && !needspick(youmonst.data) &&
!In_sokoban(&u.uz)) {
if (still_chewing(x,y)) return FALSE;
} else
if (moverock() < 0) return FALSE;
} else if (mode == TEST_TRAV) {
struct obj* obj;
/* don't pick two boulders in a row, unless there's a way thru */
if (sobj_at(BOULDER,ux,uy) && !In_sokoban(&u.uz)) {
if (!Passes_walls &&
!(tunnels(youmonst.data) && !needspick(youmonst.data)) &&
!carrying(PICK_AXE) && !carrying(DWARVISH_MATTOCK) &&
!((obj = carrying(WAN_DIGGING)) &&
!objects[obj->otyp].oc_name_known))
return FALSE;
}
}
/* test_only will assume you'll be able to push it when you get there... */
/* assume you'll be able to push it when you get there... */
}
/* OK, it is a legal place to move. */
return TRUE;
}
static void findtravelpath()
/*
* Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy).
* A shortest path is returned. If guess is TRUE, consider various
* inaccessible locations as valid intermediate path points.
* Returns TRUE if a path was found.
*/
static boolean
findtravelpath(guess)
boolean guess;
{
if ( u.tx != u.ux || u.ty != u.uy ) {
if (u.tx != u.ux || u.ty != u.uy) {
xchar travel[COLNO][ROWNO];
xchar travelstepx[2][COLNO*ROWNO];
xchar travelstepy[2][COLNO*ROWNO];
int n=1;
int set=0;
int dia=1;
xchar tx, ty, ux, uy;
int n = 1; /* max offset in travelsteps */
int set = 0; /* two sets current and previous */
int radius = 1; /* search radius */
int i;
(void) memset((genericptr_t)travel,0,sizeof(travel));
/* If guessing, first find an "obvious" goal location. The obvious
* goal is the position the player knows of, or might figure out
* (couldsee) that is closest to the target on a straight path.
*/
if (guess) {
tx = u.ux; ty = u.uy; ux = u.tx; uy = u.ty;
} else {
tx = u.tx; ty = u.ty; ux = u.ux; uy = u.uy;
}
travelstepx[0][0] = u.tx;
travelstepy[0][0] = u.ty;
while ( n ) {
int i;
int nn=0;
for (i=0; i<n; i++) {
noguess:
(void) memset((genericptr_t)travel, 0, sizeof(travel));
travelstepx[0][0] = tx;
travelstepy[0][0] = ty;
while (n != 0) {
int nn = 0;
for (i = 0; i < n; i++) {
int dir;
int x = travelstepx[set][i];
int y = travelstepy[set][i];
static int ordered[] = { 0, 2, 4, 6, 1, 3, 5, 7 };
for (dir=0; dir<8; dir++) {
for (dir = 0; dir < 8; dir++) {
int nx = x+xdir[ordered[dir]];
int ny = y+ydir[ordered[dir]];
/*printf("try %d,%d\n",nx,ny);*/
if ( isok(nx,ny) && test_move( x, y, nx-x, ny-y, 1 ) ) {
if ( nx == u.ux && ny == u.uy ) {
u.dx = x-u.ux;
u.dy = y-u.uy;
/*printf("found\n");*/
/*printf("findtravelpath %d,%d -> %d,%d by %d,%d\n",u.ux,u.uy,u.tx,u.ty,u.dx,u.dy);
*/
return;
} else {
/*printf("%d %d %d",isok(nx,ny), !travel[nx][ny], ACCESSIBLE(levl[nx][ny].typ));*/
if ( !travel[nx][ny] ) {
travelstepx[1-set][nn]=nx;
travelstepy[1-set][nn]=ny;
travel[nx][ny]=dia;
nn++;
if (!isok(nx, ny)) continue;
if (test_move(x, y, nx-x, ny-y, TEST_TRAV) &&
(levl[nx][ny].seenv || (!Blind && couldsee(nx, ny)))) {
if (nx == ux && ny == uy) {
if (!guess) {
u.dx = x-ux;
u.dy = y-uy;
if (x == u.tx && y == u.ty) {
nomul(0);
/* reset run so domove run checks work */
flags.run = 8;
}
return TRUE;
}
} else if (!travel[nx][ny]) {
travelstepx[1-set][nn] = nx;
travelstepy[1-set][nn] = ny;
travel[nx][ny] = radius;
nn++;
}
}
}
}
n = nn;
set = 1-set;
dia++;
radius++;
}
/* give up */
/* if guessing, find best location in travel matrix and go there */
if (guess) {
int px = tx, py = ty; /* pick location */
int dist, d;
dist = distmin(ux, uy, tx, ty);
for (tx = 1; tx < COLNO; ++tx)
for (ty = 0; ty < ROWNO; ++ty)
if (travel[tx][ty]) {
d = distmin(ux, uy, tx, ty);
if (d < dist && couldsee(ux, uy)) {
px = tx; py = ty; dist = d;
}
}
if (px == u.ux && py == u.uy) {
/* no guesses, just go in the general direction */
u.dx = sgn(u.tx - u.ux);
u.dy = sgn(u.ty - u.uy);
if (test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE))
return TRUE;
goto done;
}
tx = px;
ty = py;
ux = u.ux;
uy = u.uy;
set = 0;
n = radius = 1;
guess = FALSE;
goto noguess;
}
return FALSE;
}
done:
u.dx = 0;
u.dy = 0;
nomul(0);
return FALSE;
}
void
@@ -736,8 +819,9 @@ domove()
u_wipe_engr(rnd(5));
if ( flags.travel )
findtravelpath();
if (flags.travel)
if (!findtravelpath(FALSE))
(void) findtravelpath(TRUE);
if(((wtcap = near_capacity()) >= OVERLOADED
|| (wtcap > SLT_ENCUMBER &&
@@ -823,7 +907,10 @@ domove()
nomul(0);
return;
}
if((trap = t_at(x, y)) && trap->tseen) {
if (((trap = t_at(x, y)) && trap->tseen) ||
(Blind && !Levitation && !Flying &&
!is_clinger(youmonst.data) &&
(is_pool(x, y) || is_lava(x, y)) && levl[x][y].seenv)) {
if(flags.run >= 2) {
nomul(0);
flags.move = 0;
@@ -1121,7 +1208,7 @@ domove()
return;
}
if ( !test_move( u.ux, u.uy, x-u.ux, y-u.uy, 0 ) ) {
if (!test_move(u.ux, u.uy, x-u.ux, y-u.uy, DO_MOVE)) {
flags.move = 0;
nomul(0);
return;