diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 25fd65dc4..9c108add6 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -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 diff --git a/include/extern.h b/include/extern.h index 345442345..e73c931a6 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/include/youprop.h b/include/youprop.h index d71ec8b5c..c0eb10eef 100644 --- a/include/youprop.h +++ b/include/youprop.h @@ -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))) \ diff --git a/src/apply.c b/src/apply.c index 0fa842636..5a7ba6d19 100644 --- a/src/apply.c +++ b/src/apply.c @@ -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 { diff --git a/src/ball.c b/src/ball.c index 762de85f8..3ac723866 100644 --- a/src/ball.c +++ b/src/ball.c @@ -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); } diff --git a/src/cmd.c b/src/cmd.c index 54054f3c3..34d3ee19a 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -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 */ diff --git a/src/dig.c b/src/dig.c index a89b354be..1cdaee380 100644 --- a/src/dig.c +++ b/src/dig.c @@ -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); } diff --git a/src/do.c b/src/do.c index 81bbb6235..db67a34d9 100644 --- a/src/do.c +++ b/src/do.c @@ -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; diff --git a/src/do_wear.c b/src/do_wear.c index 269370e81..7ab53a79e 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -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); diff --git a/src/eat.c b/src/eat.c index 895cdf2a6..3e74d435f 100644 --- a/src/eat.c +++ b/src/eat.c @@ -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; diff --git a/src/end.c b/src/end.c index fd5d73db9..7b10d2c0e 100644 --- a/src/end.c +++ b/src/end.c @@ -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; diff --git a/src/hack.c b/src/hack.c index 1a83ae3d9..44351f4d7 100644 --- a/src/hack.c +++ b/src/hack.c @@ -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 */ 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 */ 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(); } diff --git a/src/mhitu.c b/src/mhitu.c index 7ae3ff103..f3fecbd92 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -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(); diff --git a/src/mklev.c b/src/mklev.c index aecba88fa..11067b931 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -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 */ diff --git a/src/music.c b/src/music.c index cb63bde90..b2dee2cad 100644 --- a/src/music.c +++ b/src/music.c @@ -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)); diff --git a/src/polyself.c b/src/polyself.c index c7f9c538d..a3bea6a24 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -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 */ diff --git a/src/pray.c b/src/pray.c index 5d0d456fc..bd8a3604e 100644 --- a/src/pray.c +++ b/src/pray.c @@ -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() */ diff --git a/src/teleport.c b/src/teleport.c index 615292d5d..a1adfd95d 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -289,7 +289,7 @@ boolean allow_drag; } } } - u.utrap = 0; + reset_utrap(FALSE); u.ustuck = 0; u.ux0 = u.ux; u.uy0 = u.uy; diff --git a/src/trap.c b/src/trap.c index 056570906..85d8c1a2e 100644 --- a/src/trap.c +++ b/src/trap.c @@ -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 */ diff --git a/src/zap.c b/src/zap.c index 7a5ab3416..5781d8789 100644 --- a/src/zap.c +++ b/src/zap.c @@ -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."); } }