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,