iron balls/teleds

Fixing some iron ball/teleds stuff:
-- If the player can pass through walls, ignore all checks for walls, or else
things will behave weirdly.
-- Instead of using the kludge "if the distance is >2 it must be a teleport",
pass a parameter indicating whether they crawled or teleported onto the new
space.  This fixes a special case, where the player moved one space and the
ball didn't move, but the chain moved through solid rock.  This is acceptable
if teleporting and unacceptable if dragging.
This also required some rearrangement of teleds() so that u.ux,u.uy
are set after placing the ball, not before.  I'm still not sure the pit
filling line is in the right place; anyone know?
-- add some comments so I can look at the code in a month and still know what
I did.
Most of this patch is just adding the new parameter.
This commit is contained in:
arromdee
2002-04-01 05:18:28 +00:00
parent 7c224da1e4
commit 08c463759c
11 changed files with 125 additions and 81 deletions

View File

@@ -109,7 +109,7 @@ E void NDECL(unplacebc);
E void FDECL(set_bc, (int));
E void FDECL(move_bc, (int,int,XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P));
E boolean FDECL(drag_ball, (XCHAR_P,XCHAR_P,
int *,xchar *,xchar *,xchar *,xchar *, boolean *));
int *,xchar *,xchar *,xchar *,xchar *, boolean *,BOOLEAN_P));
E void FDECL(drop_ball, (XCHAR_P,XCHAR_P));
E void NDECL(drag_down);
@@ -1870,8 +1870,8 @@ E void FDECL(place_monster, (struct monst *,int,int));
E boolean FDECL(goodpos, (int,int,struct monst *));
E boolean FDECL(enexto, (coord *,XCHAR_P,XCHAR_P,struct permonst *));
E void FDECL(teleds, (int,int));
E boolean NDECL(safe_teleds);
E void FDECL(teleds, (int,int,BOOLEAN_P));
E boolean FDECL(safe_teleds, (BOOLEAN_P));
E boolean FDECL(teleport_pet, (struct monst *,BOOLEAN_P));
E void NDECL(tele);
E int NDECL(dotele);

View File

@@ -1361,7 +1361,7 @@ int magic; /* 0=Physical, otherwise skill level */
if (In_sokoban(&u.uz))
change_luck(-1);
teleds(cc.x, cc.y);
teleds(cc.x, cc.y, TRUE);
nomul(-1);
nomovemsg = "";
morehungry(rnd(25));
@@ -2180,7 +2180,7 @@ struct obj *obj;
if (proficient && rn2(proficient + 2)) {
if (!mtmp || enexto(&cc, rx, ry, youmonst.data)) {
You("yank yourself out of the pit!");
teleds(cc.x, cc.y);
teleds(cc.x, cc.y, TRUE);
u.utrap = 0;
vision_full_recalc = 1;
}

View File

@@ -347,15 +347,26 @@ xchar ballx, bally, chainx, chainy; /* only matter !before */
*
* 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.
*
* It is called if we are moving. It is also called if we are teleporting
* *if* the ball doesn't move and we thus must drag the chain. It is not
* called for ordinary teleportation.
*
* allow_drag is only used in the ugly special case where teleporting must
* drag the chain, while an identical-looking movement must drag both the ball
* and chain.
*/
boolean
drag_ball(x, y, bc_control, ballx, bally, chainx, chainy, cause_delay)
drag_ball(x, y, bc_control, ballx, bally, chainx, chainy, cause_delay,
allow_drag)
xchar x, y;
int *bc_control;
xchar *ballx, *bally, *chainx, *chainy;
boolean *cause_delay;
boolean allow_drag;
{
struct trap *t = (struct trap *)0;
boolean already_in_rock;
*ballx = uball->ox;
*bally = uball->oy;
@@ -375,7 +386,7 @@ boolean *cause_delay;
*bc_control = BC_CHAIN;
move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
if (carried(uball)) {
/* move chain only if necessary; assume they didn't teleport */
/* move chain only if necessary */
if (distmin(x, y, uchain->ox, uchain->oy) > 1) {
*chainx = u.ux;
*chainy = u.uy;
@@ -388,17 +399,26 @@ boolean *cause_delay;
(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.
* undo the move_bc() and jump to the drag ball code. Note that this also
* means the "cannot carry and drag" message will not appear, since unless we
* moved at least two squares there is no possibility of the chain position
* being in solid rock.
*/
#define SKIP_TO_DRAG { *chainx = oldchainx; *chainy = oldchainy; \
move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \
goto drag; }
if (IS_CHAIN_ROCK(u.ux, u.uy) || IS_CHAIN_ROCK(*chainx, *chainy)
|| IS_CHAIN_ROCK(uball->ox, uball->oy))
already_in_rock = TRUE;
else
already_in_rock = FALSE;
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))
if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
SKIP_TO_DRAG;
break;
@@ -423,37 +443,44 @@ boolean *cause_delay;
tempy2 = uball->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;
!IS_CHAIN_ROCK(tempx2, tempy2) &&
!already_in_rock) {
if (allow_drag) {
/* Avoid pathological case *if* not teleporting:
* 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 *if* not teleporting:
* 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;
IS_CHAIN_ROCK(tempx2, tempy2) &&
!already_in_rock) {
if (allow_drag) {
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)) {
IS_CHAIN_ROCK(tempx2, tempy2) &&
!already_in_rock) {
SKIP_TO_DRAG;
} else if (dist2(tempx, tempy, uchain->ox, uchain->oy) <
dist2(tempx2, tempy2, uchain->ox, uchain->oy) ||
@@ -475,7 +502,7 @@ boolean *cause_delay;
break;
*chainx = (x + uball->ox)/2;
*chainy = (y + uball->oy)/2;
if (IS_CHAIN_ROCK(*chainx, *chainy))
if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
SKIP_TO_DRAG;
break;
@@ -494,7 +521,7 @@ boolean *cause_delay;
*chainx = uball->ox;
else
*chainy = uball->oy;
if (IS_CHAIN_ROCK(*chainx, *chainy))
if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
SKIP_TO_DRAG;
break;
}

View File

@@ -400,7 +400,7 @@ int dest, how;
if (enexto(&xy, etmp->ex, etmp->ey, etmp->edata)) {
pline("A %s force teleports you away...",
Hallucination ? "normal" : "strange");
teleds(xy.x, xy.y);
teleds(xy.x, xy.y, FALSE);
}
/* otherwise on top of the drawbridge is the
* only viable spot in the dungeon, so stay there

View File

@@ -953,7 +953,7 @@ boolean at_stairs, falling, portal;
pline("A mysterious force momentarily surrounds you...");
if (on_level(newlevel, &u.uz)) {
(void) safe_teleds();
(void) safe_teleds(FALSE);
(void) next_to_u();
return;
} else

View File

@@ -1126,7 +1126,7 @@ domove()
/* Move ball and chain. */
if (Punished)
if (!drag_ball(x,y, &bc_control, &ballx, &bally, &chainx, &chainy,
&cause_delay))
&cause_delay, TRUE))
return;
/* Check regions entering/leaving */

View File

@@ -363,7 +363,7 @@ mattacku(mtmp)
newsym(mtmp->mx,mtmp->my);
place_monster(mtmp, u.ux, u.uy);
if(mtmp->wormno) worm_move(mtmp);
teleds(cc.x, cc.y);
teleds(cc.x, cc.y, TRUE);
set_apparxy(mtmp);
newsym(u.ux,u.uy);
} else {

View File

@@ -219,7 +219,7 @@ register int trouble;
/* teleport should always succeed, but if not,
* just untrap them.
*/
if(!safe_teleds())
if(!safe_teleds(FALSE))
u.utrap = 0;
break;
case TROUBLE_STARVING:
@@ -256,7 +256,7 @@ register int trouble;
case TROUBLE_STUCK_IN_WALL:
Your("surroundings change.");
/* no control, but works on no-teleport levels */
(void) safe_teleds();
(void) safe_teleds(FALSE);
break;
case TROUBLE_CURSED_LEVITATION:
if (uarmf && uarmf->otyp==LEVITATION_BOOTS

View File

@@ -332,7 +332,7 @@ mount_steed(mtmp, force)
}
u.usteed = mtmp;
remove_monster(mtmp->mx, mtmp->my);
teleds(mtmp->mx, mtmp->my);
teleds(mtmp->mx, mtmp->my, TRUE);
return (TRUE);
}
@@ -568,7 +568,7 @@ dismount_steed(reason)
* teleds() clears u.utrap
*/
in_steed_dismounting = TRUE;
teleds(cc.x, cc.y);
teleds(cc.x, cc.y, TRUE);
in_steed_dismounting = FALSE;
/* Put your steed in your trap */

View File

@@ -201,34 +201,50 @@ boolean trapok;
}
void
teleds(nux, nuy)
teleds(nux, nuy, allow_drag)
register int nux,nuy;
boolean allow_drag;
{
boolean dont_teleport_ball = FALSE;
boolean ball_still_in_range = FALSE;
/* If they have to move the ball, then drag if allow_drag is true;
* otherwise they are teleporting, so unplacebc().
* If they don't have to move the ball, then always "drag" whether or
* not allow_drag is true, because we are calling that function, not
* to drag, but to move the chain. *However* there are some dumb
* special cases:
* 0 0
* _X move east -----> X_
* @ @
* These are permissible if teleporting, but not if dragging. As a
* result, drag_ball() needs to know about allow_drag and might end
* up dragging the ball anyway. Also, drag_ball() might find that
* dragging the ball is completely impossible (ball in range but there's
* rock in the way), in which case it teleports the ball on its own.
*/
if (Punished) {
/* If they're teleporting to a position where the ball doesn't need
* to be moved, don't place the ball. Especially useful when this
* function is being called for crawling out of water instead of
* real teleportation.
*/
if (!carried(uball) && distmin(nux, nuy, uball->ox, uball->oy) <= 2)
dont_teleport_ball = TRUE;
else
unplacebc();
ball_still_in_range = TRUE; /* don't have to move the ball */
else {
/* have to move the ball */
if (!allow_drag || distmin(u.ux, u.uy, nux, nuy) > 1) {
/* we should not have dist > 1 and allow_drag at the same
* time, but just in case, we must then revert to teleport.
*/
allow_drag = FALSE;
unplacebc();
}
}
}
u.utrap = 0;
u.ustuck = 0;
u.ux0 = u.ux;
u.uy0 = u.uy;
u.ux = nux;
u.uy = nuy;
fill_pit(u.ux0, u.uy0); /* do this now so that cansee() is correct */
if (hides_under(youmonst.data))
u.uundetected = OBJ_AT(nux, nuy);
else if (youmonst.data->mlet == S_EEL)
u.uundetected = is_pool(u.ux, u.uy);
u.uundetected = is_pool(nux, nuy);
else {
u.uundetected = 0;
/* mimics stop being unnoticed */
@@ -241,25 +257,24 @@ register int nux,nuy;
docrt();
}
if (Punished) {
if (dont_teleport_ball) {
if (ball_still_in_range || allow_drag) {
int bc_control;
xchar ballx, bally, chainx, chainy;
boolean cause_delay;
/* 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))
if (drag_ball(nux, nuy, &bc_control, &ballx, &bally,
&chainx, &chainy, &cause_delay, allow_drag))
move_bc(0, bc_control, ballx, bally, chainx, chainy);
} else
placebc();
}
}
/* must set u.ux, u.uy after drag_ball(), which may need to know
the old position if allow_drag is true... */
u.ux = nux;
u.uy = nuy;
fill_pit(u.ux0, u.uy0);
if (Punished) {
if (!ball_still_in_range && !allow_drag)
placebc();
}
initrack(); /* teleports mess up tracking monsters without this */
update_player_regions();
@@ -286,7 +301,8 @@ register int nux,nuy;
}
boolean
safe_teleds()
safe_teleds(allow_drag)
boolean allow_drag;
{
register int nux, nuy, tcnt = 0;
@@ -296,7 +312,7 @@ safe_teleds()
} while (!teleok(nux, nuy, (boolean)(tcnt > 200)) && ++tcnt <= 400);
if (tcnt <= 400) {
teleds(nux, nuy);
teleds(nux, nuy, allow_drag);
return TRUE;
} else
return FALSE;
@@ -309,7 +325,7 @@ vault_tele()
coord c;
if (croom && somexy(croom, &c) && teleok(c.x,c.y,FALSE)) {
teleds(c.x,c.y);
teleds(c.x,c.y,FALSE);
return;
}
tele();
@@ -394,14 +410,14 @@ tele()
/* possible extensions: introduce a small error if
magic power is low; allow transfer to solid rock */
if (teleok(cc.x, cc.y, FALSE)) {
teleds(cc.x, cc.y);
teleds(cc.x, cc.y, FALSE);
return;
}
pline("Sorry...");
}
}
(void) safe_teleds();
(void) safe_teleds(FALSE);
}
int

View File

@@ -2642,7 +2642,7 @@ crawl:;
You("dump some of your gear to lose weight...");
if (succ) {
pline("Pheew! That was close.");
teleds(x,y);
teleds(x,y,TRUE);
return(TRUE);
}
/* still too much weight */
@@ -2655,7 +2655,7 @@ crawl:;
"pool of water" : "moat";
done(DROWNING);
/* oops, we're still alive. better get out of the water. */
while (!safe_teleds()) {
while (!safe_teleds(TRUE)) {
pline("You're still drowning.");
done(DROWNING);
}
@@ -2758,7 +2758,8 @@ struct trap *ttmp;
boolean unused;
/* we know there's no monster in the way, and we're not trapped */
if (!Punished || drag_ball(x, y, &bc, &bx, &by, &cx, &cy, &unused)) {
if (!Punished || drag_ball(x, y, &bc, &bx, &by, &cx, &cy, &unused,
TRUE)) {
u.ux0 = u.ux, u.uy0 = u.uy;
u.ux = x, u.uy = y;
u.umoved = TRUE;
@@ -3618,7 +3619,7 @@ lava_effects()
killer = lava_killer;
You("burn to a crisp...");
done(BURNING);
while (!safe_teleds()) {
while (!safe_teleds(TRUE)) {
pline("You're still burning.");
done(BURNING);
}