]> granicus.if.org Git - nethack/commitdiff
remove prescient travel command behavior
authorcohrs <cohrs>
Sat, 13 Apr 2002 16:00:13 +0000 (16:00 +0000)
committercohrs <cohrs>
Sat, 13 Apr 2002 16:00:13 +0000 (16:00 +0000)
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

doc/Guidebook.mn
doc/Guidebook.tex
doc/fixes34.1
include/extern.h
include/hack.h
src/cmd.c
src/hack.c

index 05ec127bee1c0c082f29531c17462566af1f4d4c..fcd533a48e3fb1e7d8400b2ac3256ab57fdbbe31 100644 (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 
index 7a4be6bbf5e4a89cb4007cbfb35ef0004224fe45..8b53be13227b2311f81bd5bef9632265811171c2 100644 (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 
index 373d67f61b5938b9a4d8ff36b08a0a2e8203028d..ed4493e883ecec3e8b542c726aa7655ebd5db1a2 100644 (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
index 57094c4d079727dd7f96c8442906df63d3578d74..cb8ce080346d21a324033e055f3d878f86e43a9e 100644 (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));
index 3a3aadffb68288af998633cf0b33a4b8cd50b125..794b900b285515c01783261efc07cf29de1a8bf2 100644 (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')
index 3070c8d6305889ec0e8d8d7d2b9447befd713c9d..cbedd15694c9ede72c74b83ed6bc1b7247c88374 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -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)) {
index ac84a943995d73017563b7e8043086e52affdd94..7a579f386c4aca06baea422c5833bdbe53cf63e3 100644 (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;
-
-       (void) memset((genericptr_t)travel,0,sizeof(travel));
-
-       travelstepx[0][0] = u.tx;
-       travelstepy[0][0] = u.ty;
-       while ( n ) {
-           int i;
-           int nn=0;
-           for (i=0; i<n; i++) {
+       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;
+
+       /* 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;
+       }
+
+    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;