fix #H6925 - being trapped vs Levitation/Flying

Make being trapped in/on/over floor block Levitation and Flying, the
way that being inside solid rock already does, and the way levitating
blocks flight.

Blocked levitation still provides enhanced carrying capacity since
magic is attempting to make the hero's body be bouyant.  I think that
that is appropriate but am not completely convinced.

One thing that almost certainly needs fixing is digging a hole when
trapped in the floor or tethered to a buried iron ball, where the
first part of digactualhole() releases the hero from being trapped.
If being released re-enables blocked levitation, the further stages
of digging might not make sense in some circumstances.

I recently realized that being held by a grabbing monster is similar
to being trapped so should also interfere with levitation and flying.
Nothing here attempts to address that.

Save files change, but in a compatible fashion unless trapped at the
time of saving.  If someone saves while trapped prior to this patch,
then applies it and restores, the game will behave as if the patch
wasn't in place--until escape from trap is achieved.  (Not verified.)
This commit is contained in:
PatR
2018-09-30 01:06:59 -07:00
parent 00dfed5b3a
commit 3e19858edd
20 changed files with 330 additions and 149 deletions

View File

@@ -152,6 +152,11 @@ fix missing space in "would flyif you weren't levitating"
a wand of polymorph lost its magical ability for the turn just because the
player using it to engrave happened to be blind, which didn't make
a much sense
floating eye is classified as a flyer but flying is blocked while levitating,
so don't set intrinsic flying if hero is polymorphed into one
being trapped (bear trap, web, molten or solidified lava, chained to buried
iron ball) blocks both levitation and flight (note: being stuck in a
pit ends when either of those starts so doesn't apply)
Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository

View File

@@ -2438,6 +2438,8 @@ E struct monst *FDECL(animate_statue,
(struct obj *, XCHAR_P, XCHAR_P, int, int *));
E struct monst *FDECL(activate_statue_trap,
(struct trap *, XCHAR_P, XCHAR_P, BOOLEAN_P));
E void FDECL(set_utrap, (unsigned, unsigned));
E void FDECL(reset_utrap, (BOOLEAN_P));
E void FDECL(dotrap, (struct trap *, unsigned));
E void FDECL(seetrap, (struct trap *));
E void FDECL(feeltrap, (struct trap *));

View File

@@ -209,8 +209,12 @@
#define ETeleport_control u.uprops[TELEPORT_CONTROL].extrinsic
#define Teleport_control (HTeleport_control || ETeleport_control)
/* HLevitation has I_SPECIAL set if levitating due to blessed potion
which allows player to use the '>' command to end levitation early */
#define HLevitation u.uprops[LEVITATION].intrinsic
#define ELevitation u.uprops[LEVITATION].extrinsic
/* BLevitation has I_SPECIAL set if trapped in the floor,
FROMOUTSIDE set if inside solid rock (or in water on Plane of Water) */
#define BLevitation u.uprops[LEVITATION].blocked
#define Levitation ((HLevitation || ELevitation) && !BLevitation)
/* Can't touch surface, can't go under water; overrides all others */
@@ -219,8 +223,11 @@
&& (HLevitation & ~(I_SPECIAL | TIMEOUT)) == 0L \
&& (ELevitation & ~W_ARTI) == 0L)
/* Flying is overridden by Levitation */
#define HFlying u.uprops[FLYING].intrinsic
#define EFlying u.uprops[FLYING].extrinsic
/* BFlying has I_SPECIAL set if levitating or trapped in the floor or both,
FROMOUTSIDE set if inside solid rock (or in water on Plane of Water) */
#define BFlying u.uprops[FLYING].blocked
#define Flying \
((HFlying || EFlying || (u.usteed && is_flyer(u.usteed->data))) \

View File

@@ -1752,7 +1752,7 @@ int magic; /* 0=Physical, otherwise skill level */
break;
case TT_LAVA:
You("pull yourself above the %s!", hliquid("lava"));
u.utrap = 0;
reset_utrap(TRUE);
return 1;
case TT_BURIEDBALL:
case TT_INFLOOR:
@@ -2738,7 +2738,7 @@ struct obj *obj;
if (!mtmp || enexto(&cc, rx, ry, youmonst.data)) {
You("yank yourself out of the pit!");
teleds(cc.x, cc.y, TRUE);
u.utrap = 0;
reset_utrap(TRUE);
vision_full_recalc = 1;
}
} else {

View File

@@ -703,11 +703,12 @@ xchar x, y;
}
if (x != u.ux || y != u.uy) {
static const char *pullmsg = "The ball pulls you out of the %s!";
struct trap *t;
const char *pullmsg = "The ball pulls you out of the %s!";
long side;
if (u.utrap && u.utraptype != TT_INFLOOR
&& u.utraptype != TT_BURIEDBALL) {
if (u.utrap
&& u.utraptype != TT_INFLOOR && u.utraptype != TT_BURIEDBALL) {
switch (u.utraptype) {
case TT_PIT:
pline(pullmsg, "pit");
@@ -720,8 +721,8 @@ xchar x, y;
case TT_LAVA:
pline(pullmsg, hliquid("lava"));
break;
case TT_BEARTRAP: {
register long side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
case TT_BEARTRAP:
side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
pline(pullmsg, "bear trap");
set_wounded_legs(side, rn1(1000, 500));
if (!u.usteed) {
@@ -734,8 +735,7 @@ xchar x, y;
}
break;
}
}
u.utrap = 0;
reset_utrap(TRUE);
fill_pit(u.ux, u.uy);
}

View File

@@ -803,6 +803,7 @@ wiz_makemap(VOID_ARGS)
ballrelease(FALSE);
unplacebc();
}
reset_utrap(FALSE); /* also done by safe_teleds() for new level */
check_special_room(TRUE);
dmonsfree();
savelev(-1, ledger_no(&u.uz), FREE_SAVE);
@@ -2458,9 +2459,19 @@ int final;
long save_BLev = BLevitation;
BLevitation = 0L;
if (Levitation)
enl_msg(You_, "would levitate", "would have levitated",
if_surroundings_permitted, "");
if (Levitation) {
/* either trapped in the floor or inside solid rock
(or both if chained to buried iron ball and have
moved one step into solid rock somehow) */
boolean trapped = (save_BLev & I_SPECIAL) != 0L,
terrain = (save_BLev & FROMOUTSIDE) != 0L;
Sprintf(buf, "%s%s%s",
trapped ? " if not trapped" : "",
(trapped && terrain) ? " and" : "",
terrain ? if_surroundings_permitted : "");
enl_msg(You_, "would levitate", "would have levitated", buf, "");
}
BLevitation = save_BLev;
}
/* actively flying handled earlier as a status condition */
@@ -2468,15 +2479,27 @@ int final;
long save_BFly = BFlying;
BFlying = 0L;
if (Flying)
if (Flying) {
enl_msg(You_, "would fly", "would have flown",
/* wording quibble: for past tense, "hadn't been"
would sound better than "weren't" (and
"had permitted" better than "permitted"), but
"weren't" and "permitted" are adequate so the
extra complexity to handle that isn't worth it */
Levitation
? " if you weren't levitating"
: (save_BFly == FROMOUTSIDE)
? if_surroundings_permitted
/* both surroundings and [latent] levitation */
: " if circumstances permitted",
: (save_BFly == I_SPECIAL)
/* this is an oversimpliction; being trapped
might also be blocking levitation so flight
would still be blocked after escaping trap */
? " if you weren't trapped"
: (save_BFly == FROMOUTSIDE)
? if_surroundings_permitted
/* two or more of levitation, surroundings,
and being trapped in the floor */
: " if circumstances permitted",
"");
}
BFlying = save_BFly;
}
/* actively walking on water handled earlier as a status condition */

View File

@@ -339,8 +339,8 @@ dig(VOID_ARGS)
} else {
You("destroy the bear trap with %s.",
yobjnam(uwep, (const char *) 0));
u.utrap = 0; /* release from trap */
deltrap(ttmp);
reset_utrap(TRUE); /* release from trap, maybe Lev or Fly */
}
/* we haven't made any progress toward a pit yet */
context.digging.effort = 0;
@@ -557,7 +557,7 @@ int ttyp;
if (u.utraptype == TT_BURIEDBALL)
buried_ball_to_punishment();
else if (u.utraptype == TT_INFLOOR)
u.utrap = 0;
reset_utrap(FALSE);
}
/* these furniture checks were in dighole(), but wand
@@ -619,11 +619,10 @@ int ttyp;
if (at_u) {
if (!wont_fall) {
u.utrap = rn1(4, 2);
u.utraptype = TT_PIT;
set_utrap(rn1(4, 2), TT_PIT);
vision_full_recalc = 1; /* vision limits change */
} else
u.utrap = 0;
reset_utrap(TRUE);
if (oldobjs != newobjs) /* something unearthed */
(void) pickup(1); /* detects pit */
} else if (mtmp) {
@@ -1103,7 +1102,7 @@ struct obj *obj;
pline("You clear some debris from between the pits.");
}
} else if (u.utrap && u.utraptype == TT_PIT
&& (trap_with_u = t_at(u.ux, u.uy))) {
&& (trap_with_u = t_at(u.ux, u.uy)) != 0) {
You("swing %s, but the rubble has no place to go.",
yobjnam(obj, (char *) 0));
} else {
@@ -1775,8 +1774,7 @@ buried_ball_to_punishment()
(void) stop_timer(RUST_METAL, obj_to_any(ball));
#endif
punish(ball); /* use ball as flag for unearthed buried ball */
u.utrap = 0;
u.utraptype = 0;
reset_utrap(FALSE);
del_engr_at(cc.x, cc.y);
newsym(cc.x, cc.y);
}
@@ -1800,8 +1798,7 @@ buried_ball_to_freedom()
#endif
place_object(ball, cc.x, cc.y);
stackobj(ball);
u.utrap = 0;
u.utraptype = 0;
reset_utrap(TRUE);
del_engr_at(cc.x, cc.y);
newsym(cc.x, cc.y);
}

View File

@@ -171,7 +171,7 @@ const char *verb;
"squished under a boulder", NO_KILLER_PREFIX);
return FALSE; /* player remains trapped */
} else
u.utrap = 0;
reset_utrap(TRUE);
}
}
if (*verb) {
@@ -1239,7 +1239,7 @@ boolean at_stairs, falling, portal;
check_special_room(TRUE); /* probably was a trap door */
if (Punished)
unplacebc();
u.utrap = 0; /* needed in level_tele */
reset_utrap(FALSE); /* needed in level_tele */
fill_pit(u.ux, u.uy);
u.ustuck = 0; /* idem */
u.uinwater = 0;

View File

@@ -185,10 +185,11 @@ Boots_on(VOID_ARGS)
incr_itimeout(&HFumbling, rnd(20));
break;
case LEVITATION_BOOTS:
if (!oldprop && !HLevitation && !BLevitation) {
if (!oldprop && !HLevitation && !(BLevitation & FROMOUTSIDE)) {
makeknown(uarmf->otyp);
float_up();
spoteffects(FALSE);
if (Levitation)
spoteffects(FALSE); /* for sink effect */
} else {
float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
}
@@ -238,7 +239,7 @@ Boots_off(VOID_ARGS)
HFumbling = EFumbling = 0;
break;
case LEVITATION_BOOTS:
if (!oldprop && !HLevitation && !BLevitation
if (!oldprop && !HLevitation && !(BLevitation & FROMOUTSIDE)
&& !context.takeoff.cancelled_don) {
(void) float_down(0L, 0L);
makeknown(otyp);
@@ -902,10 +903,11 @@ register struct obj *obj;
}
break;
case RIN_LEVITATION:
if (!oldprop && !HLevitation && !BLevitation) {
if (!oldprop && !HLevitation && !(BLevitation & FROMOUTSIDE)) {
float_up();
learnring(obj, TRUE);
spoteffects(FALSE); /* for sinks */
if (Levitation)
spoteffects(FALSE); /* for sinks */
} else {
float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
}
@@ -1016,7 +1018,7 @@ boolean gone;
}
break;
case RIN_LEVITATION:
if (!BLevitation) {
if (!(BLevitation & FROMOUTSIDE)) {
(void) float_down(0L, 0L);
if (!Levitation)
learnring(obj, TRUE);

View File

@@ -3088,15 +3088,17 @@ int corpsecheck; /* 0, no check, 1, corpses, 2, tinnable corpses */
struct trap *ttmp = t_at(u.ux, u.uy);
if (ttmp && ttmp->tseen && ttmp->ttyp == BEAR_TRAP) {
boolean u_in_beartrap = (u.utrap && u.utraptype == TT_BEARTRAP);
/* If not already stuck in the trap, perhaps there should
be a chance to becoming trapped? Probably not, because
then the trap would just get eaten on the _next_ turn... */
Sprintf(qbuf, "There is a bear trap here (%s); eat it?",
(u.utrap && u.utraptype == TT_BEARTRAP) ? "holding you"
: "armed");
u_in_beartrap ? "holding you" : "armed");
if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
u.utrap = u.utraptype = 0;
deltrap(ttmp);
if (u_in_beartrap)
reset_utrap(TRUE);
return mksobj(BEARTRAP, TRUE, FALSE);
} else if (c == 'q') {
return (struct obj *) 0;

View File

@@ -870,7 +870,7 @@ int how;
else
multi = -1;
if (u.utrap && u.utraptype == TT_LAVA)
u.utrap = 0;
reset_utrap(FALSE);
context.botl = 1;
u.ugrave_arise = NON_PM;
HUnchanging = 0L;

View File

@@ -539,11 +539,17 @@ dosinkfall()
int dmg;
boolean lev_boots = (uarmf && uarmf->otyp == LEVITATION_BOOTS),
innate_lev = ((HLevitation & (FROMOUTSIDE | FROMFORM)) != 0L),
ufall = (!innate_lev && !(HFlying || EFlying)); /* BFlying */
/* to handle being chained to buried iron ball, trying to
levitate but being blocked, then moving onto adjacent sink;
no need to worry about being blocked by terrain because we
couldn't be over a sink at the same time */
blockd_lev = (BLevitation == I_SPECIAL),
ufall = (!innate_lev && !blockd_lev
&& !(HFlying || EFlying)); /* BFlying */
if (!ufall) {
You(innate_lev ? "wobble unsteadily for a moment."
: "gain control of your flight.");
You((innate_lev || blockd_lev) ? "wobble unsteadily for a moment."
: "gain control of your flight.");
} else {
long save_ELev = ELevitation, save_HLev = HLevitation;
@@ -1179,6 +1185,10 @@ struct trap *desttrap; /* nonnull if another trap at <x,y> */
if (!u.utrap)
return TRUE; /* sanity check */
/*
* Note: caller should call reset_utrap() when we set u.utrap to 0.
*/
switch (u.utraptype) {
case TT_BEARTRAP:
if (flags.verbose) {
@@ -1203,7 +1213,6 @@ struct trap *desttrap; /* nonnull if another trap at <x,y> */
break;
case TT_WEB:
if (uwep && uwep->oartifact == ART_STING) {
u.utrap = 0;
pline("Sting cuts through the web!");
break; /* escape trap but don't move */
}
@@ -1656,7 +1665,12 @@ domove()
return;
if (u.utrap) {
if (!trapmove(x, y, trap))
boolean moved = trapmove(x, y, trap);
if (!u.utrap)
reset_utrap(TRUE); /* might resume levitation or flight */
/* might not have escaped, or did escape but remain in same spot */
if (!moved)
return;
}
@@ -1927,13 +1941,15 @@ switch_terrain()
|| (Is_waterlevel(&u.uz) && lev->typ == WATER));
if (blocklev) {
/* called from spoteffects(), skip float_down() */
/* called from spoteffects(), stop levitating but skip float_down() */
if (Levitation)
You_cant("levitate in here.");
BLevitation |= FROMOUTSIDE;
} else if (BLevitation) {
BLevitation &= ~FROMOUTSIDE;
if (Levitation)
/* we're probably levitating now; if not, we must be chained
to a buried iron ball so get float_up() feedback for that */
if (Levitation || BLevitation)
float_up();
}
/* the same terrain that blocks levitation also blocks flight */
@@ -2058,7 +2074,7 @@ boolean pick;
spotterrain = levl[u.ux][u.uy].typ;
spotloc.x = u.ux, spotloc.y = u.uy;
/* moving onto different terrain might cause Levitation to toggle */
/* moving onto different terrain might cause Lev or Fly to toggle */
if (spotterrain != levl[u.ux0][u.uy0].typ || !on_level(&u.uz, &u.uz0))
switch_terrain();
@@ -2890,7 +2906,7 @@ boolean k_format;
int
weight_cap()
{
long carrcap, save_ELev = ELevitation;
long carrcap, save_ELev = ELevitation, save_BLev = BLevitation;
/* boots take multiple turns to wear but any properties they
confer are enabled at the start rather than the end; that
@@ -2900,6 +2916,9 @@ weight_cap()
ELevitation &= ~W_ARMF;
float_vs_flight(); /* in case Levitation is blocking Flying */
}
/* levitation is blocked by being trapped in the floor, but it still
functions enough in that situation to enhance carrying capacity */
BLevitation &= ~I_SPECIAL;
carrcap = 25 * (ACURRSTR + ACURR(A_CON)) + 50;
if (Upolyd) {
@@ -2930,8 +2949,9 @@ weight_cap()
carrcap = 0;
}
if (ELevitation != save_ELev) {
if (ELevitation != save_ELev || BLevitation != save_BLev) {
ELevitation = save_ELev;
BLevitation = save_BLev;
float_vs_flight();
}

View File

@@ -1774,7 +1774,7 @@ struct attack *mattk;
if (u.utrap) {
You("are released from the %s!",
u.utraptype == TT_WEB ? "web" : "trap");
u.utrap = 0;
reset_utrap(FALSE);
}
i = number_leashed();

View File

@@ -1683,9 +1683,9 @@ mkinvokearea()
/* any trap hero is stuck in will be going away now */
if (u.utrap) {
u.utrap = 0;
if (u.utraptype == TT_BURIEDBALL)
buried_ball_to_punishment();
reset_utrap(FALSE);
}
mkinvpos(xmin, ymin, 0); /* middle, before placing stairs */

View File

@@ -254,14 +254,10 @@ int force;
start_y = u.uy - (force * 2);
end_x = u.ux + (force * 2);
end_y = u.uy + (force * 2);
if (start_x < 1)
start_x = 1;
if (start_y < 1)
start_y = 1;
if (end_x >= COLNO)
end_x = COLNO - 1;
if (end_y >= ROWNO)
end_y = ROWNO - 1;
start_x = max(start_x, 1);
start_y = max(start_y, 0);
end_x = min(end_x, COLNO - 1);
end_y = min(end_y, ROWNO - 1);
for (x = start_x; x <= end_x; x++)
for (y = start_y; y <= end_y; y++) {
if ((mtmp = m_at(x, y)) != 0) {
@@ -311,6 +307,11 @@ int force;
break; /* no pit if portal at that location */
chasm->tseen = 1;
/* TODO:
* This ought to be split into a separate routine to
* reduce indentation and the consequent line-wraps.
*/
levl[x][y].doormask = 0;
/*
* Let liquid flow into the newly created chasm.
@@ -328,8 +329,8 @@ int force;
if ((otmp = sobj_at(BOULDER, x, y)) != 0) {
if (cansee(x, y))
pline("KADOOM! The boulder falls into a chasm%s!",
((x == u.ux) && (y == u.uy)) ? " below you"
: "");
(x == u.ux && y == u.uy) ? " below you"
: "");
if (mtmp)
mtmp->mtrapped = 0;
obj_extract_self(otmp);
@@ -343,6 +344,7 @@ int force;
if (!is_flyer(mtmp->data)
&& !is_clinger(mtmp->data)) {
boolean m_already_trapped = mtmp->mtrapped;
mtmp->mtrapped = 1;
if (!m_already_trapped) { /* suppress messages */
if (cansee(x, y))
@@ -375,6 +377,16 @@ int force;
}
}
} else if (x == u.ux && y == u.uy) {
if (u.utrap && u.utraptype == TT_BURIEDBALL) {
/* Note: the chain should break if a pit gets
created at the buried ball's location, which
is not necessarily here. But if we don't do
things this way, entering the new pit below
will override current trap anyway, but too
late to get Lev and Fly handling. */
Your("chain breaks!");
reset_utrap(TRUE);
}
if (Levitation || Flying
|| is_clinger(youmonst.data)) {
if (!tu_pit) { /* no pit here previously */
@@ -384,10 +396,9 @@ int force;
} else if (!tu_pit || !u.utrap
|| (u.utrap && u.utraptype != TT_PIT)) {
/* no pit here previously, or you were
not in it even it there was */
not in it even if there was */
You("fall into a chasm!");
u.utrap = rn1(6, 2);
u.utraptype = TT_PIT;
set_utrap(rn1(6, 2), TT_PIT);
losehp(Maybe_Half_Phys(rnd(6)),
"fell into a chasm", NO_KILLER_PREFIX);
selftouch("Falling, you");
@@ -396,9 +407,9 @@ int force;
((Fumbling && !rn2(5))
|| (!rnl(Role_if(PM_ARCHEOLOGIST) ? 3 : 9))
|| ((ACURR(A_DEX) > 7) && rn2(5)));
You("are jostled around violently!");
u.utrap = rn1(6, 2);
u.utraptype = TT_PIT; /* superfluous */
set_utrap(rn1(6, 2), TT_PIT);
losehp(Maybe_Half_Phys(rnd(keepfooting ? 2 : 4)),
"hurt in a chasm", NO_KILLER_PREFIX);
if (keepfooting)
@@ -605,8 +616,8 @@ struct obj *instr;
You("beat a deafening row!");
incr_itimeout(&HDeaf, rn1(20, 30));
exercise(A_WIS, FALSE);
} else
You("%s %s.",
} else
You("%s %s.",
rn2(2) ? "butcher" : rn2(2) ? "manage" : "pull off",
an(beats[rn2(SIZE(beats))]));
awaken_monsters(u.ulevel * (mundane ? 5 : 40));

View File

@@ -86,7 +86,10 @@ set_uasmon()
PROPSET(TELEPORT, can_teleport(mdat));
PROPSET(TELEPORT_CONTROL, control_teleport(mdat));
PROPSET(LEVITATION, is_floater(mdat));
PROPSET(FLYING, is_flyer(mdat));
/* floating eye is the only 'floater'; it is also flagged as a 'flyer';
suppress flying for it so that enlightenment doesn't confusingly
show latent flight capability always blocked by levitation */
PROPSET(FLYING, (is_flyer(mdat) && !is_floater(mdat)));
PROPSET(SWIMMING, is_swimmer(mdat));
/* [don't touch MAGICAL_BREATHING here; both Amphibious and Breathless
key off of it but include different monster forms...] */
@@ -116,12 +119,21 @@ set_uasmon()
void
float_vs_flight()
{
/* floating overrides flight; normally float_up() and float_down()
handle this, but sometimes they're skipped */
if (HLevitation || ELevitation)
boolean stuck_in_floor = (u.utrap && u.utraptype != TT_PIT);
/* floating overrides flight; so does being trapped in the floor */
if ((HLevitation || ELevitation)
|| ((HFlying || EFlying) && stuck_in_floor))
BFlying |= I_SPECIAL;
else
BFlying &= ~I_SPECIAL;
/* being trapped on the ground (bear trap, web, molten lava survived
with fire resistance, former lava solidified via cold, tethered
to a buried iron ball) overrides floating--the floor is reachable */
if ((HLevitation || ELevitation) && stuck_in_floor)
BLevitation |= I_SPECIAL;
else
BLevitation &= ~I_SPECIAL;
context.botl = TRUE;
}
@@ -207,8 +219,8 @@ const char *fmt, *arg;
if (u.twoweap && !could_twoweap(youmonst.data))
untwoweapon();
if (u.utraptype == TT_PIT && u.utrap) {
u.utrap = rn1(6, 2); /* time to escape resets */
if (u.utrap && u.utraptype == TT_PIT) {
set_utrap(rn1(6, 2), TT_PIT); /* time to escape resets */
}
if (was_blind && !Blind) { /* reverting from eyeless */
Blinded = 1L;
@@ -725,8 +737,8 @@ int mntmp;
drop_weapon(1);
(void) hideunder(&youmonst);
if (u.utraptype == TT_PIT && u.utrap) {
u.utrap = rn1(6, 2); /* time to escape resets */
if (u.utrap && u.utraptype == TT_PIT) {
set_utrap(rn1(6, 2), TT_PIT); /* time to escape resets */
}
if (was_blind && !Blind) { /* previous form was eyeless */
Blinded = 1L;
@@ -794,17 +806,17 @@ int mntmp;
spoteffects(TRUE);
if (Passes_walls && u.utrap
&& (u.utraptype == TT_INFLOOR || u.utraptype == TT_BURIEDBALL)) {
u.utrap = 0;
if (u.utraptype == TT_INFLOOR)
if (u.utraptype == TT_INFLOOR) {
pline_The("rock seems to no longer trap you.");
else {
} else {
pline_The("buried ball is no longer bound to you.");
buried_ball_to_freedom();
}
reset_utrap(TRUE);
} else if (likes_lava(youmonst.data) && u.utrap
&& u.utraptype == TT_LAVA) {
u.utrap = 0;
pline_The("%s now feels soothing.", hliquid("lava"));
reset_utrap(TRUE);
}
if (amorphous(youmonst.data) || is_whirly(youmonst.data)
|| unsolid(youmonst.data)) {
@@ -823,11 +835,11 @@ int mntmp;
You("are no longer stuck in the %s.",
u.utraptype == TT_WEB ? "web" : "bear trap");
/* probably should burn webs too if PM_FIRE_ELEMENTAL */
u.utrap = 0;
reset_utrap(TRUE);
}
if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) {
You("orient yourself on the web.");
u.utrap = 0;
reset_utrap(TRUE);
}
check_strangling(TRUE); /* maybe start strangling */

View File

@@ -355,7 +355,7 @@ int trouble;
You("are back on solid ground.");
/* teleport should always succeed, but if not, just untrap them */
if (!safe_teleds(FALSE))
u.utrap = 0;
reset_utrap(TRUE);
break;
case TROUBLE_STARVING:
/* temporarily lost strength recovery now handled by init_uhunger() */

View File

@@ -289,7 +289,7 @@ boolean allow_drag;
}
}
}
u.utrap = 0;
reset_utrap(FALSE);
u.ustuck = 0;
u.ux0 = u.ux;
u.uy0 = u.uy;

View File

@@ -805,10 +805,12 @@ struct trap *trap;
{
boolean isyou = (mtmp == &youmonst);
struct permonst *mptr = mtmp->data;
if (amorphous(mptr) || is_whirly(mptr) || flaming(mptr)
|| unsolid(mptr) || mptr == &mons[PM_GELATINOUS_CUBE]) {
xchar x = trap->tx;
xchar y = trap->ty;
if (flaming(mptr) || acidic(mptr)) {
if (domsg) {
if (isyou)
@@ -825,9 +827,9 @@ struct trap *trap;
return TRUE;
}
if (domsg) {
if (isyou)
if (isyou) {
You("flow through %s spider web.", a_your[trap->madeby_u]);
else {
} else {
pline("%s flows through %s spider web.", Monnam(mtmp),
a_your[trap->madeby_u]);
seetrap(trap);
@@ -853,6 +855,37 @@ struct trap *trap;
return otmp;
}
void
set_utrap(tim, typ)
unsigned tim, typ;
{
u.utrap = tim;
/* FIXME:
* utraptype==0 is bear trap rather than 'none'; we probably ought
* to change that but can't do so until save file compatability is
* able to be broken.
*/
u.utraptype = tim ? typ : 0;
float_vs_flight(); /* maybe block Lev and/or Fly */
}
void
reset_utrap(msg)
boolean msg;
{
boolean was_Lev = (Levitation != 0), was_Fly = (Flying != 0);
set_utrap(0, 0);
if (msg) {
if (!was_Lev && Levitation)
float_up();
if (!was_Fly && Flying)
You("can fly.");
}
}
void
dotrap(trap, trflags)
register struct trap *trap;
@@ -1042,13 +1075,12 @@ unsigned trflags;
A_Your[trap->madeby_u]);
break;
}
u.utrap = rn1(4, 4);
u.utraptype = TT_BEARTRAP;
set_utrap((unsigned) rn1(4, 4), TT_BEARTRAP);
if (u.usteed) {
pline("%s bear trap closes on %s %s!", A_Your[trap->madeby_u],
s_suffix(mon_nam(u.usteed)), mbodypart(u.usteed, FOOT));
if (thitm(0, u.usteed, (struct obj *) 0, dmg, FALSE))
u.utrap = 0; /* steed died, hero not trapped */
reset_utrap(TRUE); /* steed died, hero not trapped */
} else {
pline("%s bear trap closes on your %s!", A_Your[trap->madeby_u],
body_part(FOOT));
@@ -1191,8 +1223,12 @@ unsigned trflags;
} else
You("%s %s!", conj_pit ? "step" : "land", predicament);
}
u.utrap = rn1(6, 2);
u.utraptype = TT_PIT;
/* FIXME:
* if hero gets killed here, setting u.utrap in advance will
* show "you were trapped in a pit" during disclosure's display
* of enlightenment, but hero is dying *before* becoming trapped.
*/
set_utrap((unsigned) rn1(6, 2), TT_PIT);
if (!steedintrap(trap, (struct obj *) 0)) {
if (ttype == SPIKED_PIT) {
oldumort = u.umortality;
@@ -1286,11 +1322,13 @@ unsigned trflags;
}
You("%s %s spider web!", verbbuf, a_your[trap->madeby_u]);
}
u.utraptype = TT_WEB;
/* time will be adjusted below */
set_utrap(1, TT_WEB);
/* Time stuck in the web depends on your/steed strength. */
{
register int str = ACURR(A_STR);
int tim, str = ACURR(A_STR);
/* If mounted, the steed gets trapped. Use mintrap
* to do all the work. If mtrapped is set as a result,
@@ -1312,32 +1350,34 @@ unsigned trflags;
if (strongmonst(u.usteed->data))
str = 17;
} else {
reset_utrap(FALSE);
break;
}
webmsgok = FALSE; /* mintrap printed the messages */
}
if (str <= 3)
u.utrap = rn1(6, 6);
tim = rn1(6, 6);
else if (str < 6)
u.utrap = rn1(6, 4);
tim = rn1(6, 4);
else if (str < 9)
u.utrap = rn1(4, 4);
tim = rn1(4, 4);
else if (str < 12)
u.utrap = rn1(4, 2);
tim = rn1(4, 2);
else if (str < 15)
u.utrap = rn1(2, 2);
tim = rn1(2, 2);
else if (str < 18)
u.utrap = rnd(2);
tim = rnd(2);
else if (str < 69)
u.utrap = 1;
tim = 1;
else {
u.utrap = 0;
tim = 0;
if (webmsgok)
You("tear through %s web!", a_your[trap->madeby_u]);
deltrap(trap);
newsym(u.ux, u.uy); /* get rid of trap symbol */
}
set_utrap((unsigned) tim, TT_WEB);
}
break;
@@ -2767,16 +2807,34 @@ float_up()
context.botl = TRUE;
if (u.utrap) {
if (u.utraptype == TT_PIT) {
u.utrap = 0;
reset_utrap(FALSE);
You("float up, out of the pit!");
vision_full_recalc = 1; /* vision limits change */
fill_pit(u.ux, u.uy);
} else if (u.utraptype == TT_INFLOOR) {
} else if (u.utraptype == TT_LAVA /* molten lava */
|| u.utraptype == TT_INFLOOR) { /* solidified lava */
Your("body pulls upward, but your %s are still stuck.",
makeplural(body_part(LEG)));
} else {
You("float up, only your %s is still stuck.", body_part(LEG));
} else if (u.utraptype == TT_BURIEDBALL) { /* tethered */
coord cc;
cc.x = u.ux, cc.y = u.uy;
/* caveat: this finds the first buried iron ball within
one step of the specified location, not necessarily the
buried [former] uball at the original anchor point */
(void) buried_ball(&cc);
/* being chained to the floor blocks levitation from floating
above that floor but not from enhancing carrying capacity */
You("feel lighter, but your %s is still chained to the %s.",
body_part(LEG),
IS_ROOM(levl[cc.x][cc.y].typ) ? "floor" : "ground");
} else if (u.utraptype == WEB) {
You("float up slightly, but you are still stuck in the web.");
} else { /* bear trap */
You("float up slightly, but your %s is still stuck.",
body_part(LEG));
}
/* when still trapped, float_vs_flight() below will block levitation */
#if 0
} else if (Is_waterlevel(&u.uz)) {
pline("It feels as though you've lost some weight.");
@@ -2795,8 +2853,7 @@ float_up()
} else {
You("start to float in the air!");
}
if (u.usteed && !is_floater(u.usteed->data)
&& !is_flyer(u.usteed->data)) {
if (u.usteed && !is_floater(u.usteed->data) && !is_flyer(u.usteed->data)) {
if (Lev_at_will) {
pline("%s magically floats up!", Monnam(u.usteed));
} else {
@@ -2806,7 +2863,7 @@ float_up()
}
if (Flying)
You("are no longer able to control your flight.");
BFlying |= I_SPECIAL;
float_vs_flight(); /* set BFlying, also BLevitation if still trapped */
/* levitation gives maximum carrying capacity, so encumbrance
state might be reduced */
(void) encumber_msg();
@@ -2841,16 +2898,29 @@ long hmask, emask; /* might cancel timeout */
if (Levitation)
return 0; /* maybe another ring/potion/boots */
if (BLevitation) {
/* Levitation is blocked, so hero is not actually floating
hence shouldn't have float_down effects and feedback */
float_vs_flight(); /* before nomul() rather than after */
/* if blocked by terrain, we haven't actually been levitating so
we don't give any end-of-levitation feedback or side-effects,
but if blocking is solely due to being trapped in/on floor,
do give some feedback but skip other float_down() effects */
boolean trapped = (BLevitation == I_SPECIAL);
float_vs_flight();
if (trapped && u.utrap) /* u.utrap => paranoia */
You("are no longer trying to float up from the %s.",
(u.utraptype == TT_BEARTRAP) ? "trap's jaws"
: (u.utraptype == TT_WEB) ? "web"
: (u.utraptype == TT_BURIEDBALL) ? "chain"
: (u.utraptype == TT_LAVA) ? "lava"
: "ground"); /* TT_INFLOOR */
(void) encumber_msg(); /* carrying capacity might have changed */
return 0;
}
context.botl = TRUE;
nomul(0); /* stop running or resting */
if (BFlying) {
/* controlled flight no longer overridden by levitation */
BFlying &= ~I_SPECIAL;
float_vs_flight(); /* clears BFlying & I_SPECIAL
* unless hero is stuck in floor */
if (Flying) {
You("have stopped levitating and are now flying.");
(void) encumber_msg(); /* carrying capacity might have changed */
@@ -2981,7 +3051,7 @@ climb_pit()
if (Passes_walls) {
/* marked as trapped so they can pick things up */
You("ascend from the pit.");
u.utrap = 0;
reset_utrap(FALSE);
fill_pit(u.ux, u.uy);
vision_full_recalc = 1; /* vision limits change */
} else if (!rn2(2) && sobj_at(BOULDER, u.ux, u.uy)) {
@@ -2993,10 +3063,11 @@ climb_pit()
/* eg fell in pit, then poly'd to a flying monster;
or used '>' to deliberately enter it */
You("%s from the pit.", Flying ? "fly" : "climb");
u.utrap = 0;
reset_utrap(FALSE);
fill_pit(u.ux, u.uy);
vision_full_recalc = 1; /* vision limits change */
} else if (!(--u.utrap)) {
reset_utrap(FALSE);
You("%s to the edge of the pit.",
(Sokoban && Levitation)
? "struggle against the air currents and float"
@@ -3887,7 +3958,7 @@ boolean bury_it;
}
newsym(ttmp->tx, ttmp->ty);
if (u.utrap && ttmp->tx == u.ux && ttmp->ty == u.uy)
u.utrap = 0;
reset_utrap(TRUE);
deltrap(ttmp);
}
@@ -4506,51 +4577,80 @@ struct monst *mon;
boolean *noticed; /* set to true iff hero notices the effect; */
{ /* otherwise left with its previous value intact */
struct trap *t;
char buf[BUFSZ];
const char *trapdescr, *which;
char buf[BUFSZ], whichbuf[20];
const char *trapdescr = 0, *which = 0;
boolean ishero = (mon == &youmonst);
if (!mon)
return FALSE;
if (mon == u.usteed)
ishero = TRUE;
t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my);
/* if no trap here or it's not a holding trap, we're done */
if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB))
return FALSE;
trapdescr = defsyms[trap_to_defsym(t->ttyp)].explanation;
which = t->tseen ? the_your[t->madeby_u]
: index(vowels, *trapdescr) ? "an" : "a";
t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my);
if (ishero && u.utrap) { /* all u.utraptype values are holding traps */
which = "";
switch (u.utraptype) {
case TT_LAVA:
trapdescr = "molten lava";
break;
case TT_INFLOOR:
/* solidified lava, so not "floor" even if within a room */
trapdescr = "ground";
break;
case TT_BURIEDBALL:
trapdescr = "your anchor";
break;
case TT_BEARTRAP:
case TT_PIT:
case TT_WEB:
trapdescr = 0; /* use defsyms[].explanation */
break;
default:
/* lint suppression in case 't' is unexpectedly Null
or u.utraptype has new value we don't know about yet */
trapdescr = "trap";
break;
}
} else {
/* if no trap here or it's not a holding trap, we're done */
if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB))
return FALSE;
}
if (!trapdescr)
trapdescr = defsyms[trap_to_defsym(t->ttyp)].explanation;
if (!which)
which = t->tseen ? the_your[t->madeby_u]
: index(vowels, *trapdescr) ? "an" : "a";
if (*which)
which = strcat(strcpy(whichbuf, which), " ");
if (ishero) {
if (!u.utrap)
return FALSE;
u.utrap = 0; /* released regardless of type */
*noticed = TRUE;
/* give message only if trap was the expected type */
if (u.utraptype == TT_BEARTRAP || u.utraptype == TT_WEB) {
if (u.usteed)
Sprintf(buf, "%s is", noit_Monnam(u.usteed));
else
Strcpy(buf, "You are");
pline("%s released from %s %s.", buf, which, trapdescr);
}
if (u.usteed)
Sprintf(buf, "%s is", noit_Monnam(u.usteed));
else
Strcpy(buf, "You are");
pline("%s released from %s%s.", buf, which, trapdescr);
reset_utrap(TRUE);
} else {
if (!mon->mtrapped)
return FALSE;
mon->mtrapped = 0;
if (canspotmon(mon)) {
*noticed = TRUE;
pline("%s is released from %s %s.", Monnam(mon), which,
pline("%s is released from %s%s.", Monnam(mon), which,
trapdescr);
} else if (cansee(t->tx, t->ty) && t->tseen) {
*noticed = TRUE;
if (t->ttyp == WEB)
pline("%s is released from %s %s.", Something, which,
pline("%s is released from %s%s.", Something, which,
trapdescr);
else /* BEAR_TRAP */
pline("%s %s opens.", upstart(strcpy(buf, which)), trapdescr);
pline("%s%s opens.", upstart(strcpy(buf, which)), trapdescr);
}
/* might pacify monster if adjacent */
if (rn2(2) && distu(mon->mx, mon->my) <= 2)
@@ -5002,8 +5102,8 @@ register struct trap *ttmp;
register struct monst *mtmp;
if (ttmp->tx == u.ux && ttmp->ty == u.uy) {
u.utrap = 0;
u.utraptype = 0;
if (u.utraptype != TT_BURIEDBALL)
reset_utrap(TRUE);
} else if ((mtmp = m_at(ttmp->tx, ttmp->ty)) != 0) {
mtmp->mtrapped = 0;
}
@@ -5211,12 +5311,11 @@ lava_effects()
boil_away = !Fire_resistance;
/* if not fire resistant, sink_into_lava() will quickly be fatal;
hero needs to escape immediately */
u.utrap = rn1(4, 4) + ((boil_away ? 2 : rn1(4, 12)) << 8);
u.utraptype = TT_LAVA;
set_utrap((unsigned) (rn1(4, 4) + ((boil_away ? 2 : rn1(4, 12)) << 8)),
TT_LAVA);
You("sink into the %s%s!", hliquid("lava"),
!boil_away
? ", but it only burns slightly"
: " and are about to be immolated");
!boil_away ? ", but it only burns slightly"
: " and are about to be immolated");
if (u.uhp > 1)
losehp(!boil_away ? 1 : (u.uhp / 2), lava_killer,
KILLED_BY); /* lava damage */
@@ -5238,7 +5337,7 @@ sink_into_lava()
if (!u.utrap || u.utraptype != TT_LAVA) {
; /* do nothing; this shouldn't happen */
} else if (!is_lava(u.ux, u.uy)) {
u.utrap = 0; /* this shouldn't happen either */
reset_utrap(FALSE); /* this shouldn't happen either */
} else if (!u.uinvulnerable) {
/* ordinarily we'd have to be fire resistant to survive long
enough to become stuck in lava, but it can happen without
@@ -5255,8 +5354,10 @@ sink_into_lava()
burn_away_slime(); /* add insult to injury? */
done(DISSOLVED);
/* can only get here via life-saving; try to get away from lava */
u.utrap = 0;
(void) safe_teleds(TRUE);
reset_utrap(TRUE);
/* levitation or flight have become unblocked, otherwise Tport */
if (!Levitation && !Flying)
(void) safe_teleds(TRUE);
} else if (!u.umoved) {
/* can't fully turn into slime while in lava, but might not
have it be burned away until you've come awfully close */

View File

@@ -4431,11 +4431,10 @@ short exploding_wand_typ;
vision_full_recalc = 1;
} else if (u.utrap && u.utraptype == TT_LAVA) {
if (Passes_walls) {
u.utrap = 0;
You("pass through the now-solid rock.");
reset_utrap(TRUE);
} else {
u.utrap = rn1(50, 20);
u.utraptype = TT_INFLOOR;
set_utrap(rn1(50, 20), TT_INFLOOR);
You("are firmly stuck in the cooling rock.");
}
}