iron balls (R676)

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.
This commit is contained in:
arromdee
2002-03-26 06:05:24 +00:00
parent 224eddc1d3
commit 9210d2c5ed
3 changed files with 88 additions and 15 deletions

View 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;
}

View 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();
}