From c1045cc18a5559cd000eb9fb4bef39d17fb27452 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 2 Nov 2023 09:12:37 -0700 Subject: [PATCH] sanity check: avoid "you over monster" impossible I did this several months ago to avoid a sanity check warning (and consequent fuzzer panic) when an engulfer expels the hero on a full level. I was hoping to refine it but never went back; install it now before forgetting about it entirely. If a chameleon changes from wall-phazer to engulfer while in a spot the hero can't move onto and engulfs him/her, expelling the hero after the engulfer has taken the hero's spot might be forced to put the hero on top of the engulfer or another monster when unable to use the engulfer's former spot. Rather than try to figure out all the possible ways this might happen and attempt to deal with each of them, just prevent an engulf attack from succeeding if the hero wouldn't be able to move to the engulfer's spot. (Does not prevent an air elemental over water from engulfing the hero.) --- doc/fixes3-7-0.txt | 4 ++++ src/mhitm.c | 38 ++++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 2c4c93eb9..8d1701bed 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1713,6 +1713,10 @@ applying a wielded lump of royal jelly but not picking something to rub it on "addinv: null obj after quiver merge" (like applying lit potion of oil) ceiling hider could become hidden when there was no ceiling, triggering a warning if sanity_check was On +hero could be forced onto a monster's location if a level was full and an + engulfer was occupying an adjacent but inaccessible spot, engulfed + hero, then expelled hero while now occupying hero's accessible spot, + triggering sanity_check warning "you over monster" experimental #saveoptions wasn't saving the set of cond_xyz options correctly; if player viewed the 'm O' submenu for status conditions, they would get saved even if not changed; otherwise, ones that came in from the diff --git a/src/mhitm.c b/src/mhitm.c index e6a3d64a5..30d96fe16 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitm.c $NHDT-Date: 1627412283 2021/07/27 18:58:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.199 $ */ +/* NetHack 3.7 mhitm.c $NHDT-Date: 1698939796 2023/11/02 15:43:16 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.244 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -797,7 +797,9 @@ boolean engulf_target(struct monst *magr, struct monst *mdef) { struct rm *lev; - int dx, dy; + int ax, ay, dx, dy; + boolean uatk = (magr == &gy.youmonst), + udef = (mdef == &gy.youmonst); /* can't swallow something that's too big */ if (mdef->data->msize >= MZ_HUGE @@ -808,21 +810,25 @@ engulf_target(struct monst *magr, struct monst *mdef) if (mdef->mtrapped || magr->mtrapped) return FALSE; - /* (hypothetical) engulfers who can pass through walls aren't - limited by rock|trees|bars */ - if ((magr == &gy.youmonst) ? Passes_walls : passes_walls(magr->data)) - return TRUE; - - /* don't swallow something in a spot where attacker wouldn't - otherwise be able to move onto; we don't want to engulf - a wall-phaser and end up with a non-phaser inside a wall */ - dx = mdef->mx, dy = mdef->my; - if (mdef == &gy.youmonst) - dx = u.ux, dy = u.uy; + /* if attacker is phasing in solid rock and defender can't move there, + or vice versa, don't allow engulf to succeeed; otherwise expelling + might not be able to place attacker and defender both back on map; + when defender is the hero, a sanity_check complaint about placing + the hero on top of a monster can occur */ + dx = (mdef == &gy.youmonst) ? u.ux : mdef->mx; + dy = (mdef == &gy.youmonst) ? u.uy : mdef->my; lev = &levl[dx][dy]; - if (IS_ROCK(lev->typ) || closed_door(dx, dy) || IS_TREE(lev->typ) - /* not passes_bars(); engulfer isn't squeezing through */ - || (lev->typ == IRONBARS && !is_whirly(magr->data))) + if (!(udef ? Passes_walls : passes_walls(mdef->data)) + && (IS_ROCK(lev->typ) || closed_door(dx, dy) || IS_TREE(lev->typ) + /* not passes_bars(); engulfer isn't squeezing through */ + || (lev->typ == IRONBARS && !is_whirly(magr->data)))) + return FALSE; + ax = (magr == &gy.youmonst) ? u.ux : magr->mx; + ay = (magr == &gy.youmonst) ? u.uy : magr->my; + lev = &levl[ax][ay]; + if (!(uatk ? Passes_walls : passes_walls(magr->data)) + && (IS_ROCK(lev->typ) || closed_door(ax, ay) || IS_TREE(lev->typ) + || (lev->typ == IRONBARS && !is_whirly(mdef->data)))) return FALSE; return TRUE;