diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index c3d7151a5..419ee5966 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1172,6 +1172,12 @@ engraving in a breach in a shop's or vault's wall or vault guard's temporary if a fire resistant non-fire immune monster wearing a thoroughly burnt wooden shield got knocked into lava, burning the shield completely yielded impossible warning "obfree: deleting worn obj" +hangup in wizard or explore mode would result in answering ESC to "Die?" + prompt if that was reached, and since default is 'no' the hero would + be life-saved and the game would try to keep going; if circumstances + resulted in repeat death then the program might get stuck in a loop + instead of exiting [no reports of such, but if it ever happened the + process was probably killed without anyone knowing why it happened] Fixes to 3.7.0-x General Problems Exposed Via git Repository @@ -1564,6 +1570,10 @@ buried troll whose auto-revive timer expired might triger panic with "revive default case 6" throwing a helm of brilliance could yield "breaking odd object?" sanity checking of engravings was stopping after first problem found +the fuzzer could get stuck in a loop if hero died in a way where life-saving + just resulted in a repeat death (cited case was burning up in lava, + where life-saving teleports you out of it; if the level is full, the + teleport will fail and you'll immediately burn up again) Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/include/decl.h b/include/decl.h index 9baadc442..c17583f52 100644 --- a/include/decl.h +++ b/include/decl.h @@ -307,6 +307,9 @@ struct instance_globals_d { /* dog.c */ char dogname[PL_PSIZ]; + /* end.c */ + long done_seq; /* for counting deaths occurring on same hero_seq */ + /* mon.c */ boolean disintegested; diff --git a/src/decl.c b/src/decl.c index a73c7d604..1ad533490 100644 --- a/src/decl.c +++ b/src/decl.c @@ -320,6 +320,8 @@ const struct instance_globals_d g_init_d = { 0, /* did_nothing_flag */ /* dog.c */ DUMMY, /* dogname */ + /* end.c */ + 0L, /* done_seq */ /* mon.c */ FALSE, /* disintegested */ /* o_init.c */ diff --git a/src/end.c b/src/end.c index b6ad6c6be..4dfe6e629 100644 --- a/src/end.c +++ b/src/end.c @@ -1173,8 +1173,23 @@ done(int how) bot(); } + /* hero_seq is (moves<<3 + n) where n is number of moves made + by the hero on the current turn (since the 'moves' variable + actually counts turns); its details shouldn't matter here */ + if (gd.done_seq < gh.hero_seq) + gd.done_seq = gh.hero_seq; + if (iflags.debug_fuzzer) { - if (!(gp.program_state.panicking || how == PANICKED)) { + if (!gp.program_state.panicking && how != PANICKED + /* Guard against getting stuck in a loop if we die in one of + * the few ways where life-saving isn't effective (cited case + * was burning in lava when the level was too full to allow + * teleporting to safety). Skip the life-save attempt if we've + * died on the same move more than 15 times; give up instead. + * (Note: theoretically we could get killed more than that in + * one move if there are multiple fast monsters with multiple + * attacks against a wimply hero, or a ton of ranged attacks.) */ + && (gd.done_seq++ < gh.hero_seq + 15L)) { savelife(how); /* periodically restore characteristics and lost exp levels or cure lycanthropy */ @@ -1192,7 +1207,8 @@ done(int how) gk.killer.format = 0; return; } - } else + } + if (how == ASCENDED || (!gk.killer.name[0] && how == GENOCIDED)) gk.killer.format = NO_KILLER_PREFIX; /* Avoid killed by "a" burning or "a" starvation */ @@ -1237,6 +1253,12 @@ done(int how) } /* explore and wizard modes offer player the option to keep playing */ if (!survive && (wizard || discover) && how <= GENOCIDED +#ifdef HANGUPHANDLING + /* if hangup has occurred, the only possible answer to a paranoid + query is 'no'; we want 'no' as the default for "Die?" but can't + accept it more than once if there's no user supplying it */ + && !(gp.program_state.done_hup && gd.done_seq++ == gh.hero_seq) +#endif && !paranoid_query(ParanoidDie, "Die?")) { pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die"); iflags.last_msg = PLNMSG_OK_DONT_DIE;