]> granicus.if.org Git - nethack/commitdiff
fix github issue #900 - "Elbereth" engravings
authorPatR <rankin@nethack.org>
Sat, 15 Oct 2022 09:13:39 +0000 (02:13 -0700)
committerPatR <rankin@nethack.org>
Sat, 15 Oct 2022 09:13:39 +0000 (02:13 -0700)
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

include/engrave.h
include/extern.h
src/engrave.c
src/monmove.c
src/teleport.c

index 6af92386093496726bde7c61be4521ad590f3d77..c09cc5eaaa81d90a5363b47465c952a893aca86c 100644 (file)
@@ -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 */
index 815bf6a4ed000f069d26b38fc3bb1f0afad1bbb1..d36dcad820b23f485b8a9f0db6c8466c5784ec5f 100644 (file)
@@ -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);
index a7dd57fb274fb471ca438ff720cab6bb476ba8a1..cae185878a871699e72cf28b0e8a14097cd18aec 100644 (file)
@@ -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;
index 51e91cc5e0420755f4ec7ebd77ea49a1bae7bf0a..c1398cd0fc68930df98b35d51df70f7abf58070e 100644 (file)
@@ -167,8 +167,10 @@ dochugw(
 }
 
 boolean
-onscary(coordxy x, coordxy y, struct monstmtmp)
+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)
index 8df35115fcd605110cba1d978e69386d92cb4a3e..97b34b3182797d3a337927c86d96b993a18bcdc9 100644 (file)
@@ -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;
 }
 
 /*