From 4328cf49ef9f779eeab4e8176b8e9ea1f183a3fc Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 3 Mar 2018 16:46:39 -0800 Subject: [PATCH] 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. --- doc/fixes36.1 | 2 ++ include/extern.h | 1 + src/pray.c | 57 +++++++++++++++++++++++++++++++++++------------- src/timeout.c | 38 +++++++++++++++++++++++++++++++- 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index c282bdc58..e1c5c4468 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -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 diff --git a/include/extern.h b/include/extern.h index 4ea4915c4..b6f482515 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 diff --git a/src/pray.c b/src/pray.c index ef61df831..29a2bd601 100644 --- a/src/pray.c +++ b/src/pray.c @@ -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)) { diff --git a/src/timeout.c b/src/timeout.c index b8535bbd5..86e01ae5f 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -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,