diff --git a/include/youprop.h b/include/youprop.h index 7ffe08d4a..c41fdba48 100644 --- a/include/youprop.h +++ b/include/youprop.h @@ -249,8 +249,9 @@ && !BFlying) /* May touch surface; does not override any others */ +#define HWwalking u.uprops[WWALKING].intrinsic /* see lava_effects() */ #define EWwalking u.uprops[WWALKING].extrinsic -#define Wwalking (EWwalking && !Is_waterlevel(&u.uz)) +#define Wwalking ((HWwalking || EWwalking) && !Is_waterlevel(&u.uz)) /* Don't get wet, can't go under water; overrides others except levitation */ /* Wwalking is meaningless on water level */ diff --git a/src/timeout.c b/src/timeout.c index 516788501..f1ec05893 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -55,13 +55,17 @@ static const struct propname { /* timed pass-walls is a potential prayer result if surrounded by stone with nowhere to be safely teleported to */ { PASSES_WALLS, "pass thru walls" }, + /* timed fire resistance and water walking are possible in explore mode + (as well as in wizard mode) after life-saving in lava if it fails to + teleport the hero to safety and player declines to die */ + { WWALKING, "water walking" }, + { FIRE_RES, "fire resistance" }, /* * Properties beyond here don't have timed values during normal play, * so there's not much point in trying to order them sensibly. * They're either on or off based on equipment, role, actions, &c, - * but in wizard mode #wizintrinsic can give then as timed effects. + * but in wizard mode, #wizintrinsic can give them as timed effects. */ - { FIRE_RES, "fire resistance" }, { COLD_RES, "cold resistance" }, { SLEEP_RES, "sleep resistance" }, { DISINT_RES, "disintegration resistance" }, @@ -86,7 +90,6 @@ static const struct propname { { JUMPING, "jumping" }, { TELEPORT_CONTROL, "teleport control" }, { FLYING, "flying" }, - { WWALKING, "water walking" }, { SWIMMING, "swimming" }, { MAGICAL_BREATHING, "magical breathing" }, { SLOW_DIGESTION, "slow digestion" }, @@ -771,6 +774,19 @@ nh_timeout(void) wielding_corpse(uswapwep, (struct obj *) 0, FALSE); } break; + case FIRE_RES: + /* timed fire resistance and timed water walking combine + as a way to survive lava after multiple life-saving + attempts fail to relocate hero; skip timeout message + if hero has acquired fire resistance in the meantime */ + if (!Fire_resistance) + Your("temporary ability to survive burning has ended."); + break; + case WWALKING: + /* [see fire reeistance] */ + if (!Wwalking) + Your("temporary ability to walk on liquid has ended."); + break; case DISPLACED: if (!Displaced) /* give a message */ toggle_displacement((struct obj *) 0, 0L, FALSE); @@ -778,12 +794,13 @@ nh_timeout(void) case WARN_OF_MON: /* timed Warn_of_mon is via #wizintrinsic only */ if (!Warn_of_mon) { + struct permonst *wptr = gc.context.warntype.species; + + gc.context.warntype.species = (struct permonst *) 0; gc.context.warntype.speciesidx = NON_PM; - if (gc.context.warntype.species) { + if (wptr) You("are no longer warned about %s.", - makeplural(gc.context.warntype.species->pmnames[NEUTRAL])); - gc.context.warntype.species = (struct permonst *) 0; - } + makeplural(wptr->pmnames[NEUTRAL])); } break; case PASSES_WALLS: @@ -1921,7 +1938,7 @@ wiz_timeout_queue(void) if ((ln = (int) strlen(propname)) > longestlen) longestlen = ln; } - if (specindx == 0 && p == FIRE_RES) + if (specindx == 0 && p == COLD_RES) /* was FIRE_RES but has changed */ specindx = i; } putstr(win, 0, ""); diff --git a/src/trap.c b/src/trap.c index 1dc9c55ac..7d0ccee36 100644 --- a/src/trap.c +++ b/src/trap.c @@ -6117,8 +6117,8 @@ boolean lava_effects(void) { register struct obj *obj, *obj2; - int lifesave_limit; boolean usurvive, boil_away; + int burncount = 0, burnmesgcount = 0; int dmg = d(6, 6); /* only applicable for water walking */ if (iflags.in_lava_effects) { @@ -6153,14 +6153,18 @@ lava_effects(void) /* Check whether we should burn away boots *first* so we know whether to * make the player sink into the lava. Assumption: water walking only * comes from boots. + * (3.7: that assumption is no longer true, but having boots be the first + * thing to come into contact with lava makes sense.) */ if (uarmf && is_organic(uarmf) && !uarmf->oerodeproof) { obj = uarmf; pline("%s into flame!", Yobjnam2(obj, "burst")); + ++burnmesgcount; iflags.in_lava_effects++; /* (see above) */ (void) Boots_off(); useup(obj); iflags.in_lava_effects--; + ++burncount; } if (!Fire_resistance) { @@ -6193,20 +6197,31 @@ lava_effects(void) The(xname(obj)), hcolor("dark red")); } else if (obj->in_use) { if (obj->owornmask) { - if (usurvive) + if (usurvive) { pline("%s into flame!", Yobjnam2(obj, "burst")); + ++burnmesgcount; + } remove_worn_item(obj, TRUE); } useupall(obj); + ++burncount; } } + if (usurvive && burncount > burnmesgcount) + pline("%s item%s in your inventory %s been destroyed.", + (burnmesgcount > 0) + ? ((burncount - burnmesgcount == 1) ? "Another" : "Other") + : ((burncount == 1) ? "An" : "Some"), + plur(burncount - burnmesgcount), + (burncount - burnmesgcount == 1) ? "has" : "have"); /* s/he died... */ boil_away = (u.umonnum == PM_WATER_ELEMENTAL || u.umonnum == PM_STEAM_VORTEX || u.umonnum == PM_FOG_CLOUD); - lifesave_limit = 20; /* prevent fuzz testing from getting stuck */ - do { + /* burn to death; if hero is life-saved on the first pass, try + to teleport to safety; if that fails, burn all over again */ + for (burncount = 0; burncount < 2; ++burncount) { u.uhp = -1; /* killer format and name are reconstructed every iteration because lifesaving resets them */ @@ -6215,23 +6230,30 @@ lava_effects(void) urgent_pline("You %s...", boil_away ? "boil away" : "burn to a crisp"); done(BURNING); - if (safe_teleds(TELEDS_ALLOW_DRAG | TELEDS_TELEPORT)) + if (safe_teleds(TELEDS_ALLOW_DRAG | TELEDS_TELEPORT) + /* if the level is completely full then this second attempt + won't accomplish anything, but if it is only mostly full + then hero still might manage to escape the lava */ + || safe_teleds(TELEDS_ALLOW_DRAG | TELEDS_TELEPORT)) break; /* successful life-save */ /* nowhere safe to land; repeat burning loop */ pline("You're still burning."); - } while (--lifesave_limit > 0); + } iflags.in_lava_effects--; - if (!lifesave_limit) { /* failed to be teleported to safety */ - gd.done_seq = 0L; /* reset the life-saves per move limit */ - /* when fuzz testing, couldn't be rescued but mustn't stay stuck - in the done(BURNING) loop; if not fuzz testing, player has - answered no to "Die?" over and over (ridiculously persistent - or maybe pasted a bunch of junk into the input buffer) */ - goto burn_stuff; /* moveloop() will kill hero again next move; - * fuzzer will eventually pick wizard mode level - * teleport for hero's randomly chosen action */ + if (burncount == 2) { + /* life-saved twice (second time must have been due to declining + to die in wizard|explore mode) and failed to be teleported + to safety both times; moveloop() will just drop the hero into + the lava again on next move so take countermeasures to give + the player--or the debug fuzzer--a chance to try something + else instead of just immediately burning up all over again */ + if (!Fire_resistance) + set_itimeout(&HFire_resistance, 5L); + if (!Wwalking) + set_itimeout(&HWwalking, 5L); + goto burn_stuff; } /*