fix prayer infinite loop

Reported internally, if a prayer resulted in 'fix all troubles' and
one of those was TROUBLE_STUCK_IN_WALL but safe_teleds() couldn't find
any place to relocate the hero to, nothing was done and STUCK_IN_WALL
would be found again as the next trouble to fix.  Since safe_teleds()
eventually resorts to trying every single spot on the map, there was
no other result possible than failing to find an available spot again,
nothing would be done, and next trouble would be STUCK_IN_WALL, ad
naseum.

I started out with a fix that looked for secret corridors to expose
and doors to open, to make more space available, then try to move a
monster off the level, then try digging out rock and/or walls and
smashing boulders.  None of those guarantee success and I got bogged
down by the digging case.  This was going to be a last resort if all
of those still failed to make somewhere to move the hero, but for now,
at least, I'm skipping all that other stuff and going directly to the
last resort:  give the hero Passes_walls ability for a short time, and
let him or her find own way out of trouble.  The next trouble to fix
won't be STUCK_IN_WALL because Passes_walls makes that a non-issue.

I'm not thrilled with the new messages involved but want to get this
behind me.
This commit is contained in:
PatR
2018-03-03 16:46:39 -08:00
parent 2111abdc29
commit 4328cf49ef
4 changed files with 82 additions and 16 deletions

View File

@@ -517,6 +517,8 @@ whatis lookup for 'more info?' would behave strangely for plural names, either
you should not hear a whistle if you are deaf
change the deity's "congratulations" message upon ascension to something which
sounds a bit more archaic to fit better with the other messages
prayer boon of 'fix all troubles' could get stuck in an infinite loop for
TROUBLE_STUCK_IN_WALL if there was no spot to teleport into available
Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository

View File

@@ -1917,6 +1917,7 @@ E const char *NDECL(bottlename);
/* ### pray.c ### */
E boolean FDECL(critically_low_hp, (BOOLEAN_P));
E boolean NDECL(stuck_in_wall);
#ifdef USE_TRAMPOLI
E int NDECL(prayer_done);
#endif

View File

@@ -140,6 +140,31 @@ boolean only_if_injured; /* determines whether maxhp <= 5 matters */
return (boolean) (curhp <= 5 || curhp * divisor <= maxhp);
}
/* return True if surrounded by impassible rock, regardless of the state
of your own location (for example, inside a doorless closet) */
boolean
stuck_in_wall()
{
int i, j, x, y, count = 0;
if (Passes_walls)
return FALSE;
for (i = -1; i <= 1; i++) {
x = u.ux + i;
for (j = -1; j <= 1; j++) {
if (!i && !j)
continue;
y = u.uy + j;
if (!isok(x, y)
|| (IS_ROCK(levl[x][y].typ)
&& (levl[x][y].typ != SDOOR || levl[x][y].typ != SCORR))
|| (blocked_boulder(i, j) && !throws_rocks(youmonst.data)))
++count;
}
}
return (count == 8) ? TRUE : FALSE;
}
/*
* Return 0 if nothing particular seems wrong, positive numbers for
* serious trouble, and negative numbers for comparative annoyances.
@@ -158,7 +183,7 @@ STATIC_OVL int
in_trouble()
{
struct obj *otmp;
int i, j, count = 0;
int i;
/*
* major troubles
@@ -183,19 +208,8 @@ in_trouble()
return TROUBLE_LYCANTHROPE;
if (near_capacity() >= EXT_ENCUMBER && AMAX(A_STR) - ABASE(A_STR) > 3)
return TROUBLE_COLLAPSING;
for (i = -1; i <= 1; i++)
for (j = -1; j <= 1; j++) {
if (!i && !j)
continue;
if (!isok(u.ux + i, u.uy + j)
|| IS_ROCK(levl[u.ux + i][u.uy + j].typ)
|| (blocked_boulder(i, j) && !throws_rocks(youmonst.data)))
count++;
}
if (count == 8 && !Passes_walls)
if (stuck_in_wall())
return TROUBLE_STUCK_IN_WALL;
if (Cursed_obj(uarmf, LEVITATION_BOOTS)
|| stuck_ring(uleft, RIN_LEVITATION)
|| stuck_ring(uright, RIN_LEVITATION))
@@ -396,9 +410,22 @@ int trouble;
}
break;
case TROUBLE_STUCK_IN_WALL:
Your("surroundings change.");
/* no control, but works on no-teleport levels */
(void) safe_teleds(FALSE);
if (safe_teleds(FALSE)) {
Your("surroundings change.");
} else {
/* safe_teleds() couldn't find a safe place; perhaps the
level is completely full. As a last resort, confer
intrinsic wall/rock-phazing. Hero might get stuck
again fairly soon....
Without something like this, fix_all_troubles can get
stuck in an infinite loop trying to fix STUCK_IN_WALL
and repeatedly failing. */
set_itimeout(&HPasses_walls, (long) d(4, 4));
/* how else could you move between packed rocks or among
lattice forming "solid" rock? */
You_feel("much slimmer.");
}
break;
case TROUBLE_CURSED_LEVITATION:
if (Cursed_obj(uarmf, LEVITATION_BOOTS)) {

View File

@@ -275,6 +275,7 @@ levitation_dialogue()
if (((HLevitation & TIMEOUT) % 2L) && i > 0L && i <= SIZE(levi_texts)) {
const char *s = levi_texts[SIZE(levi_texts) - i];
if (index(s, '%')) {
boolean danger = (is_pool_or_lava(u.ux, u.uy)
&& !Is_waterlevel(&u.uz));
@@ -336,6 +337,30 @@ burn_away_slime()
}
}
/* Intrinsic Passes_walls is temporary when your god is trying to fix
all troubles and then TROUBLE_STUCK_IN_WALL calls safe_teleds() but
it can't find anywhere to place you. If that happens you get a small
value for (HPasses_walls & TIMEOUT) to move somewhere yourself.
Message given is "you feel much slimmer" as a joke hint that you can
move between things which are closely packed--like the substance of
solid rock! */
static NEARDATA const char *const phaze_texts[] = {
"You start to feel bloated.",
"You are feeling rather flabby.",
};
STATIC_OVL void
phaze_dialogue()
{
long i = ((HPasses_walls & TIMEOUT) / 2L);
if (EPasses_walls || (HPasses_walls & ~TIMEOUT))
return;
if (((HPasses_walls & TIMEOUT) % 2L) && i > 0L && i <= SIZE(phaze_texts))
pline1(phaze_texts[SIZE(phaze_texts) - i]);
}
void
nh_timeout()
{
@@ -373,8 +398,10 @@ nh_timeout()
vomiting_dialogue();
if (Strangled)
choke_dialogue();
if (Levitation)
if (HLevitation & TIMEOUT)
levitation_dialogue();
if (HPasses_walls & TIMEOUT)
phaze_dialogue();
if (u.mtimedone && !--u.mtimedone) {
if (Unchanging)
u.mtimedone = rnd(100 * youmonst.data->mlevel + 1);
@@ -526,6 +553,15 @@ nh_timeout()
case LEVITATION:
(void) float_down(I_SPECIAL | TIMEOUT, 0L);
break;
case PASSES_WALLS:
if (!Passes_walls) {
if (stuck_in_wall())
You_feel("hemmed in again.");
else
pline("You're back to your %s self again.",
!Upolyd ? "normal" : "unusual");
}
break;
case STRANGLED:
killer.format = KILLED_BY;
Strcpy(killer.name,