diff --git a/doc/fixes34.1 b/doc/fixes34.1 index 4555a75f1..061df06e5 100644 --- a/doc/fixes34.1 +++ b/doc/fixes34.1 @@ -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 diff --git a/src/ball.c b/src/ball.c index d325d651c..364b3f832 100644 --- a/src/ball.c +++ b/src/ball.c @@ -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; } diff --git a/src/teleport.c b/src/teleport.c index 05e2d2a76..3cb68e04a 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -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(); }