fuzzer vs lava, take III
Change dying in lava to attempt life-saving at most twice. The first time might be via amulet or via declining to die. The second time can only be via declining to die after failing to teleport to safety the first time, but could happen in explore mode as well as in wizard mode, either interactive or fuzzer. If the hero dies twice without ending the game, confer temporary fire resistance and water walking so that other actions can be attempted. After 5 turns without getting away from the lava or doing something to acquire those capabilities, the hero will be subject to falling in again. Then the whole cycle might repeat, even many times, but the fuzzer will eventually choose ^V or #wizmakemap and escape. Players in explore mode will either figure out a way to get out of it or eventually have to give up but can try as many times as they like, not that much different than being cornered by a deadly monster.
This commit is contained in:
@@ -249,8 +249,9 @@
|
|||||||
&& !BFlying)
|
&& !BFlying)
|
||||||
/* May touch surface; does not override any others */
|
/* May touch surface; does not override any others */
|
||||||
|
|
||||||
|
#define HWwalking u.uprops[WWALKING].intrinsic /* see lava_effects() */
|
||||||
#define EWwalking u.uprops[WWALKING].extrinsic
|
#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 */
|
/* Don't get wet, can't go under water; overrides others except levitation */
|
||||||
/* Wwalking is meaningless on water level */
|
/* Wwalking is meaningless on water level */
|
||||||
|
|
||||||
|
|||||||
@@ -55,13 +55,17 @@ static const struct propname {
|
|||||||
/* timed pass-walls is a potential prayer result if surrounded by stone
|
/* timed pass-walls is a potential prayer result if surrounded by stone
|
||||||
with nowhere to be safely teleported to */
|
with nowhere to be safely teleported to */
|
||||||
{ PASSES_WALLS, "pass thru walls" },
|
{ 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,
|
* Properties beyond here don't have timed values during normal play,
|
||||||
* so there's not much point in trying to order them sensibly.
|
* so there's not much point in trying to order them sensibly.
|
||||||
* They're either on or off based on equipment, role, actions, &c,
|
* 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" },
|
{ COLD_RES, "cold resistance" },
|
||||||
{ SLEEP_RES, "sleep resistance" },
|
{ SLEEP_RES, "sleep resistance" },
|
||||||
{ DISINT_RES, "disintegration resistance" },
|
{ DISINT_RES, "disintegration resistance" },
|
||||||
@@ -86,7 +90,6 @@ static const struct propname {
|
|||||||
{ JUMPING, "jumping" },
|
{ JUMPING, "jumping" },
|
||||||
{ TELEPORT_CONTROL, "teleport control" },
|
{ TELEPORT_CONTROL, "teleport control" },
|
||||||
{ FLYING, "flying" },
|
{ FLYING, "flying" },
|
||||||
{ WWALKING, "water walking" },
|
|
||||||
{ SWIMMING, "swimming" },
|
{ SWIMMING, "swimming" },
|
||||||
{ MAGICAL_BREATHING, "magical breathing" },
|
{ MAGICAL_BREATHING, "magical breathing" },
|
||||||
{ SLOW_DIGESTION, "slow digestion" },
|
{ SLOW_DIGESTION, "slow digestion" },
|
||||||
@@ -771,6 +774,19 @@ nh_timeout(void)
|
|||||||
wielding_corpse(uswapwep, (struct obj *) 0, FALSE);
|
wielding_corpse(uswapwep, (struct obj *) 0, FALSE);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case DISPLACED:
|
||||||
if (!Displaced) /* give a message */
|
if (!Displaced) /* give a message */
|
||||||
toggle_displacement((struct obj *) 0, 0L, FALSE);
|
toggle_displacement((struct obj *) 0, 0L, FALSE);
|
||||||
@@ -778,12 +794,13 @@ nh_timeout(void)
|
|||||||
case WARN_OF_MON:
|
case WARN_OF_MON:
|
||||||
/* timed Warn_of_mon is via #wizintrinsic only */
|
/* timed Warn_of_mon is via #wizintrinsic only */
|
||||||
if (!Warn_of_mon) {
|
if (!Warn_of_mon) {
|
||||||
|
struct permonst *wptr = gc.context.warntype.species;
|
||||||
|
|
||||||
|
gc.context.warntype.species = (struct permonst *) 0;
|
||||||
gc.context.warntype.speciesidx = NON_PM;
|
gc.context.warntype.speciesidx = NON_PM;
|
||||||
if (gc.context.warntype.species) {
|
if (wptr)
|
||||||
You("are no longer warned about %s.",
|
You("are no longer warned about %s.",
|
||||||
makeplural(gc.context.warntype.species->pmnames[NEUTRAL]));
|
makeplural(wptr->pmnames[NEUTRAL]));
|
||||||
gc.context.warntype.species = (struct permonst *) 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PASSES_WALLS:
|
case PASSES_WALLS:
|
||||||
@@ -1921,7 +1938,7 @@ wiz_timeout_queue(void)
|
|||||||
if ((ln = (int) strlen(propname)) > longestlen)
|
if ((ln = (int) strlen(propname)) > longestlen)
|
||||||
longestlen = ln;
|
longestlen = ln;
|
||||||
}
|
}
|
||||||
if (specindx == 0 && p == FIRE_RES)
|
if (specindx == 0 && p == COLD_RES) /* was FIRE_RES but has changed */
|
||||||
specindx = i;
|
specindx = i;
|
||||||
}
|
}
|
||||||
putstr(win, 0, "");
|
putstr(win, 0, "");
|
||||||
|
|||||||
52
src/trap.c
52
src/trap.c
@@ -6117,8 +6117,8 @@ boolean
|
|||||||
lava_effects(void)
|
lava_effects(void)
|
||||||
{
|
{
|
||||||
register struct obj *obj, *obj2;
|
register struct obj *obj, *obj2;
|
||||||
int lifesave_limit;
|
|
||||||
boolean usurvive, boil_away;
|
boolean usurvive, boil_away;
|
||||||
|
int burncount = 0, burnmesgcount = 0;
|
||||||
int dmg = d(6, 6); /* only applicable for water walking */
|
int dmg = d(6, 6); /* only applicable for water walking */
|
||||||
|
|
||||||
if (iflags.in_lava_effects) {
|
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
|
/* Check whether we should burn away boots *first* so we know whether to
|
||||||
* make the player sink into the lava. Assumption: water walking only
|
* make the player sink into the lava. Assumption: water walking only
|
||||||
* comes from boots.
|
* 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) {
|
if (uarmf && is_organic(uarmf) && !uarmf->oerodeproof) {
|
||||||
obj = uarmf;
|
obj = uarmf;
|
||||||
pline("%s into flame!", Yobjnam2(obj, "burst"));
|
pline("%s into flame!", Yobjnam2(obj, "burst"));
|
||||||
|
++burnmesgcount;
|
||||||
iflags.in_lava_effects++; /* (see above) */
|
iflags.in_lava_effects++; /* (see above) */
|
||||||
(void) Boots_off();
|
(void) Boots_off();
|
||||||
useup(obj);
|
useup(obj);
|
||||||
iflags.in_lava_effects--;
|
iflags.in_lava_effects--;
|
||||||
|
++burncount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Fire_resistance) {
|
if (!Fire_resistance) {
|
||||||
@@ -6193,20 +6197,31 @@ lava_effects(void)
|
|||||||
The(xname(obj)), hcolor("dark red"));
|
The(xname(obj)), hcolor("dark red"));
|
||||||
} else if (obj->in_use) {
|
} else if (obj->in_use) {
|
||||||
if (obj->owornmask) {
|
if (obj->owornmask) {
|
||||||
if (usurvive)
|
if (usurvive) {
|
||||||
pline("%s into flame!", Yobjnam2(obj, "burst"));
|
pline("%s into flame!", Yobjnam2(obj, "burst"));
|
||||||
|
++burnmesgcount;
|
||||||
|
}
|
||||||
remove_worn_item(obj, TRUE);
|
remove_worn_item(obj, TRUE);
|
||||||
}
|
}
|
||||||
useupall(obj);
|
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... */
|
/* s/he died... */
|
||||||
boil_away = (u.umonnum == PM_WATER_ELEMENTAL
|
boil_away = (u.umonnum == PM_WATER_ELEMENTAL
|
||||||
|| u.umonnum == PM_STEAM_VORTEX
|
|| u.umonnum == PM_STEAM_VORTEX
|
||||||
|| u.umonnum == PM_FOG_CLOUD);
|
|| u.umonnum == PM_FOG_CLOUD);
|
||||||
lifesave_limit = 20; /* prevent fuzz testing from getting stuck */
|
/* burn to death; if hero is life-saved on the first pass, try
|
||||||
do {
|
to teleport to safety; if that fails, burn all over again */
|
||||||
|
for (burncount = 0; burncount < 2; ++burncount) {
|
||||||
u.uhp = -1;
|
u.uhp = -1;
|
||||||
/* killer format and name are reconstructed every iteration
|
/* killer format and name are reconstructed every iteration
|
||||||
because lifesaving resets them */
|
because lifesaving resets them */
|
||||||
@@ -6215,23 +6230,30 @@ lava_effects(void)
|
|||||||
urgent_pline("You %s...", boil_away ? "boil away"
|
urgent_pline("You %s...", boil_away ? "boil away"
|
||||||
: "burn to a crisp");
|
: "burn to a crisp");
|
||||||
done(BURNING);
|
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 */
|
break; /* successful life-save */
|
||||||
/* nowhere safe to land; repeat burning loop */
|
/* nowhere safe to land; repeat burning loop */
|
||||||
pline("You're still burning.");
|
pline("You're still burning.");
|
||||||
} while (--lifesave_limit > 0);
|
}
|
||||||
|
|
||||||
iflags.in_lava_effects--;
|
iflags.in_lava_effects--;
|
||||||
|
|
||||||
if (!lifesave_limit) { /* failed to be teleported to safety */
|
if (burncount == 2) {
|
||||||
gd.done_seq = 0L; /* reset the life-saves per move limit */
|
/* life-saved twice (second time must have been due to declining
|
||||||
/* when fuzz testing, couldn't be rescued but mustn't stay stuck
|
to die in wizard|explore mode) and failed to be teleported
|
||||||
in the done(BURNING) loop; if not fuzz testing, player has
|
to safety both times; moveloop() will just drop the hero into
|
||||||
answered no to "Die?" over and over (ridiculously persistent
|
the lava again on next move so take countermeasures to give
|
||||||
or maybe pasted a bunch of junk into the input buffer) */
|
the player--or the debug fuzzer--a chance to try something
|
||||||
goto burn_stuff; /* moveloop() will kill hero again next move;
|
else instead of just immediately burning up all over again */
|
||||||
* fuzzer will eventually pick wizard mode level
|
if (!Fire_resistance)
|
||||||
* teleport for hero's randomly chosen action */
|
set_itimeout(&HFire_resistance, 5L);
|
||||||
|
if (!Wwalking)
|
||||||
|
set_itimeout(&HWwalking, 5L);
|
||||||
|
goto burn_stuff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user