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

@@ -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

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