From: PatR Date: Sat, 15 Oct 2022 09:13:39 +0000 (-0700) Subject: fix github issue #900 - "Elbereth" engravings X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=659f16070c8282146ae116d2d436f25840b552ae;p=nethack fix github issue #900 - "Elbereth" engravings Issue reported by vultur-cadens: Elbereth used to be effective in inhibiting monster movement when an object was present on the same spot, but since 3.6.0 it isn't. It only functions that way when the hero--or hero's displaced image--is present these days. So special levels that have been using engraved Elbereth to try to protect objects from monsters haven't been providing any useful protection. This makes Elbereth that's engraved during level creation work like it used to in 3.4.3 and earlier: when there's at least one object on the engraving's spot, monsters who are affected by Elbereth will be affected. [I'm fairly sure that that behavior started out unintentionally, as a side-effect of an optimization to only check for scroll of scare monster when there was at least one item present which is a necessary condition for such a scroll.] Old-style Elbereth includes Elbereth chosen as a random engraving during level creation in addition to engravings specified in special level definitions. Engravings by the player don't have the required attribute and player-engraved Elbereth behaves in the 3.6 way. This ought to be replaced by something more general. Perhaps a new engraving type not usable by the player? Fixes #900 --- diff --git a/include/engrave.h b/include/engrave.h index 6af923860..c09cc5eaa 100644 --- a/include/engrave.h +++ b/include/engrave.h @@ -20,10 +20,15 @@ struct engr { #define ENGR_BLOOD 5 #define HEADSTONE 6 #define N_ENGRAVE 6 + Bitfield(guardobjects, 1); /* if engr_txt is "Elbereth", it is effective + * against monsters when an object is present + * even when hero isn't (so behaves similarly + * to how Elbereth did in 3.4.3) */ + /* 7 free bits */ }; #define newengr(lth) \ - (struct engr *) alloc((unsigned)(lth) + sizeof(struct engr)) -#define dealloc_engr(engr) free((genericptr_t)(engr)) + (struct engr *) alloc((unsigned) (lth) + (unsigned) sizeof (struct engr)) +#define dealloc_engr(engr) free((genericptr_t) (engr)) #endif /* ENGRAVE_H */ diff --git a/include/extern.h b/include/extern.h index 815bf6a4e..d36dcad82 100644 --- a/include/extern.h +++ b/include/extern.h @@ -809,7 +809,7 @@ extern void cant_reach_floor(coordxy, coordxy, boolean, boolean); extern const char *surface(coordxy, coordxy); extern const char *ceiling(coordxy, coordxy); extern struct engr *engr_at(coordxy, coordxy); -extern boolean sengr_at(const char *, coordxy, coordxy, boolean); +extern struct engr *sengr_at(const char *, coordxy, coordxy, boolean); extern void u_wipe_engr(int); extern void wipe_engr_at(coordxy, coordxy, xint16, boolean); extern void read_engr_at(coordxy, coordxy); diff --git a/src/engrave.c b/src/engrave.c index a7dd57fb2..cae185878 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -271,16 +271,17 @@ engr_at(coordxy x, coordxy y) * If strict checking is requested, the word is only considered to be * present if it is intact and is the entire content of the engraving. */ -boolean +struct engr * sengr_at(const char *s, coordxy x, coordxy y, boolean strict) { - register struct engr *ep = engr_at(x, y); + struct engr *ep = engr_at(x, y); if (ep && ep->engr_type != HEADSTONE && ep->engr_time <= g.moves) { - return (strict ? !strcmpi(ep->engr_txt, s) - : (strstri(ep->engr_txt, s) != 0)); + if (strict ? !strcmpi(ep->engr_txt, s) + : (strstri(ep->engr_txt, s) != 0)) + return ep; } - return FALSE; + return (struct engr *) NULL; } void @@ -395,16 +396,22 @@ make_engr_at(coordxy x, coordxy y, const char *s, long e_time, xint16 e_type) if ((ep = engr_at(x, y)) != 0) del_engr(ep); ep = newengr(smem); - (void) memset((genericptr_t)ep, 0, smem + sizeof(struct engr)); + (void) memset((genericptr_t) ep, 0, smem + sizeof (struct engr)); ep->nxt_engr = head_engr; head_engr = ep; ep->engr_x = x; ep->engr_y = y; ep->engr_txt = (char *) (ep + 1); Strcpy(ep->engr_txt, s); - /* engraving Elbereth shows wisdom */ - if (!g.in_mklev && !strcmp(s, "Elbereth")) - exercise(A_WIS, TRUE); + if (!strcmp(s, "Elbereth")) { + /* engraving "Elbereth": if done when making a level, it creates + an old-style Elbereth that deters monsters when any objects are + present; otherwise (done by the player), exercises wisdom */ + if (g.in_mklev) + ep->guardobjects = 1; + else + exercise(A_WIS, TRUE); + } ep->engr_time = e_time; ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE - 1); ep->engr_lth = smem; diff --git a/src/monmove.c b/src/monmove.c index 51e91cc5e..c1398cd0f 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -167,8 +167,10 @@ dochugw( } boolean -onscary(coordxy x, coordxy y, struct monst* mtmp) +onscary(coordxy x, coordxy y, struct monst *mtmp) { + struct engr *ep; + /* creatures who are directly resistant to magical scaring: * humans aren't monsters * uniques have ascended their base monster instincts @@ -208,15 +210,16 @@ onscary(coordxy x, coordxy y, struct monst* mtmp) * Elbereth doesn't work in Gehennom, the Elemental Planes, or the * Astral Plane; the influence of the Valar only reaches so far. */ - return (sengr_at("Elbereth", x, y, TRUE) - && (u_at(x, y) || (Displaced && mtmp->mux == x && mtmp->muy == y)) + return ((ep = sengr_at("Elbereth", x, y, TRUE)) != 0 + && (u_at(x, y) + || (Displaced && mtmp->mux == x && mtmp->muy == y) + || (ep->guardobjects && vobj_at(x, y))) && !(mtmp->isshk || mtmp->isgd || !mtmp->mcansee || mtmp->mpeaceful || mtmp->data->mlet == S_HUMAN || mtmp->data == &mons[PM_MINOTAUR] || Inhell || In_endgame(&u.uz))); } - /* regenerate lost hit points */ void mon_regen(struct monst *mon, boolean digest_meal) diff --git a/src/teleport.c b/src/teleport.c index 8df35115f..97b34b318 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -40,7 +40,8 @@ noteleport_level(struct monst* mon) } /* this is an approximation of onscary() that doesn't use any 'struct monst' - fields aside from 'monst->data' */ + fields aside from 'monst->data'; used primarily for new monster creation + and monster teleport destination, not for ordinary monster movement */ static boolean goodpos_onscary( coordxy x, coordxy y, @@ -60,10 +61,11 @@ goodpos_onscary( /* engraved Elbereth doesn't work in Gehennom or the end-game */ if (Inhell || In_endgame(&u.uz)) return FALSE; - /* creatures who don't (or can't) fear a written Elbereth */ + /* creatures who don't (or can't) fear a written Elbereth and weren't + caught by the minions check */ if (mptr == &mons[PM_MINOTAUR] || !haseyes(mptr)) return FALSE; - return sengr_at("Elbereth", x, y, TRUE); + return sengr_at("Elbereth", x, y, TRUE) ? TRUE : FALSE; } /*