]> granicus.if.org Git - nethack/commitdiff
iron balls (R676)
authorarromdee <arromdee>
Tue, 26 Mar 2002 06:05:24 +0000 (06:05 +0000)
committerarromdee <arromdee>
Tue, 26 Mar 2002 06:05:24 +0000 (06:05 +0000)
Well, this proved rather annoying.  Problems included:
-- the solid rock problem that was noticed
-- teleporting to a spot two spaces away but on the other side of solid rock
could also leave the chain in solid rock
-- in one place I said chainx instead of ballx, which could cause problems with
teleporting
-- the teleport code moved the player before moving the ball, violating the
assumption that the player hasn't been moved yet (which only caused problems
after I added the solid rock fix).

Ball movement still isn't quite right, though the cases are really rare.  I
may fix them later.

doc/fixes34.1
src/ball.c
src/teleport.c

index 4555a75f1596dcb67b02c52e4fa2c15e77d7dac4..061df06e527ef7c5eb588c2254e12716e1c9f87c 100644 (file)
@@ -17,6 +17,7 @@ wall symbol not replaced when digging while blind and levitating
 print regular death message when leashed, mounted steed dies of starvation
 fix more funny messages, new and old
 restore the behavior of bumping into closed doors when moving while impaired
+fix iron ball cases that could put the chain in solid rock
 
 
 Platform- and/or Interface-Specific Fixes
index d325d651c55c19a1ad3f9dc1ea84e52f0ad425ed..364b3f832ec35cfbeb8b9c05885e823261a10545 100644 (file)
@@ -343,7 +343,7 @@ xchar ballx, bally, chainx, chainy; /* only matter !before */
     }
 }
 
-/* return TRUE if ball could be dragged
+/* return TRUE if the caller needs to place the ball and chain down again
  *
  *  Should not be called while swallowed.  Should be called before movement,
  *  because we might want to move the ball or chain to the hero's old position.
@@ -371,6 +371,7 @@ boolean *cause_delay;
 
        /* only need to move the chain? */
        if (carried(uball) || distmin(x, y, uball->ox, uball->oy) <= 2) {
+           xchar oldchainx = uchain->ox, oldchainy = uchain->oy;
            *bc_control = BC_CHAIN;
            move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
            if (carried(uball)) {
@@ -383,11 +384,22 @@ boolean *cause_delay;
            }
 #define CHAIN_IN_MIDDLE(chx, chy) \
 (distmin(x, y, chx, chy) <= 1 && distmin(chx, chy, uball->ox, uball->oy) <= 1)
+#define IS_CHAIN_ROCK(x,y) \
+(IS_ROCK(levl[x][y].typ) || (IS_DOOR(levl[x][y].typ) && \
+      (levl[x][y].doormask & (D_CLOSED|D_LOCKED))))
+/* Don't ever move the chain into solid rock.  If we have to, then instead
+ * undo the move_bc() and jump to the drag ball code.
+ */
+#define SKIP_TO_DRAG do { *chainx = oldchainx; *chainy = oldchainy; \
+    move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \
+    goto drag; } while(0)
            switch(dist2(x, y, uball->ox, uball->oy)) {
                /* two spaces diagonal from ball, move chain inbetween */
                case 8:
                    *chainx = (uball->ox + x)/2;
                    *chainy = (uball->oy + y)/2;
+                   if (IS_CHAIN_ROCK(*chainx, *chainy))
+                       SKIP_TO_DRAG;
                    break;
 
                /* player is distance 2/1 from ball; move chain to one of the
@@ -410,7 +422,40 @@ boolean *cause_delay;
                        tempy = y;
                        tempy2 = uball->oy;
                    }
-                   if (dist2(tempx, tempy, uchain->ox, uchain->oy) <
+                   if (IS_CHAIN_ROCK(tempx, tempy) &&
+                               !IS_CHAIN_ROCK(tempx2, tempy2)) {
+                       /* Avoid pathological case:
+                        *   0                          0_
+                        *   _X  move northeast  ----->  X@
+                        *    @
+                        */
+                       if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5 &&
+                             dist2(x, y, tempx, tempy) == 1)
+                           SKIP_TO_DRAG;
+                       /* Avoid pathological case:
+                        *    0                          0
+                        *   _X  move east       ----->  X_
+                        *    @                           @
+                        */
+                       if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4 &&
+                             dist2(x, y, tempx, tempy) == 2)
+                           SKIP_TO_DRAG;
+                       *chainx = tempx2;
+                       *chainy = tempy2;
+                   } else if (!IS_CHAIN_ROCK(tempx, tempy) &&
+                               IS_CHAIN_ROCK(tempx2, tempy2)) {
+                       if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5 &&
+                               dist2(x, y, tempx2, tempy2) == 1)
+                           SKIP_TO_DRAG;
+                       if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4 &&
+                             dist2(x, y, tempx2, tempy2) == 2)
+                           SKIP_TO_DRAG;
+                       *chainx = tempx;
+                       *chainy = tempy;
+                   } else if (IS_CHAIN_ROCK(tempx, tempy) &&
+                               IS_CHAIN_ROCK(tempx2, tempy2)) {
+                       SKIP_TO_DRAG;
+                   } else if (dist2(tempx, tempy, uchain->ox, uchain->oy) <
                         dist2(tempx2, tempy2, uchain->ox, uchain->oy) ||
                       ((dist2(tempx, tempy, uchain->ox, uchain->oy) ==
                         dist2(tempx2, tempy2, uchain->ox, uchain->oy)) && rn2(2))) {
@@ -428,8 +473,10 @@ boolean *cause_delay;
                case 4:
                    if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
                        break;
-                   *chainx = (x + uchain->ox)/2;
-                   *chainy = (y + uchain->oy)/2;
+                   *chainx = (x + uball->ox)/2;
+                   *chainy = (y + uball->oy)/2;
+                   if (IS_CHAIN_ROCK(*chainx, *chainy))
+                       SKIP_TO_DRAG;
                    break;
                
                /* ball is one space diagonal from player.  Check for the
@@ -447,6 +494,8 @@ boolean *cause_delay;
                            *chainx = uball->ox;
                        else
                            *chainy = uball->oy;
+                       if (IS_CHAIN_ROCK(*chainx, *chainy))
+                           SKIP_TO_DRAG;
                        break;
                    }
                    /* fall through */
@@ -470,11 +519,15 @@ boolean *cause_delay;
                default: impossible("bad chain movement");
                    break;
            }
+#undef SKIP_TO_DRAG
+#undef IS_CHAIN_ROCK
 #undef CHAIN_IN_MIDDLE
            return TRUE;
        }
 
-       if (near_capacity() > SLT_ENCUMBER) {
+drag:
+
+       if (near_capacity() > SLT_ENCUMBER && dist2(x, y, u.ux, u.uy) <= 2) {
            You("cannot %sdrag the heavy iron ball.",
                            invent ? "carry all that and also " : "");
            nomul(0);
@@ -527,13 +580,25 @@ boolean *cause_delay;
            }
        }
 
-       *bc_control = BC_BALL|BC_CHAIN;;
+       *bc_control = BC_BALL|BC_CHAIN;
 
        move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
-       *ballx  = uchain->ox;
-       *bally  = uchain->oy;
-       *chainx = u.ux;
-       *chainy = u.uy;
+       if (dist2(x, y, u.ux, u.uy) > 2) {
+           /* Awful case: we're still in range of the ball, so we thought we
+            * could only move the chain, but it turned out that the target
+            * square for the chain was rock, so we had to drag it instead.
+            * But we can't drag it either, because we teleported and are more
+            * than one square from our old position.  Revert to the teleport
+            * behavior.
+            */
+           *ballx = *chainx = x;
+           *bally = *chainy = y;
+       } else {
+           *ballx  = uchain->ox;
+           *bally  = uchain->oy;
+           *chainx = u.ux;
+           *chainy = u.uy;
+       }
        *cause_delay = TRUE;
        return TRUE;
 }
index 05e2d2a7642d09164f5ca69aaf63466cb64d6af5..3cb68e04a793de1c5c0e256ac9b00655b14bfb98 100644 (file)
@@ -246,11 +246,18 @@ register int nux,nuy;
                xchar ballx, bally, chainx, chainy;
                boolean cause_delay;
 
-               /* this should only drag the chain (and never give a near-
-                  capacity message) since we already checked ball distance */
-               (void) drag_ball(u.ux, u.uy, &bc_control, &ballx, &bally,
-                                       &chainx, &chainy, &cause_delay);
-               move_bc(0, bc_control, ballx, bally, chainx, chainy);
+               /* We really should only be dragging the chain here, since we
+                * checked the ball distance.  However, some pathological
+                * cases will drag the ball anyway.  drag_ball() tries to
+                * handle those by ignoring near_capacity() and teleporting the
+                * ball and chain along with you.  Bug: if you only teleported
+                * one square, drag_ball() has no way to distinguish between
+                * teleporting and moving, and treats it like a move.  (Note
+                * that teleds() doesn't imply teleporting.)
+                */
+               if (drag_ball(u.ux, u.uy, &bc_control, &ballx, &bally,
+                                       &chainx, &chainy, &cause_delay))
+                   move_bc(0, bc_control, ballx, bally, chainx, chainy);
            } else
                 placebc();
        }