]> granicus.if.org Git - nethack/commitdiff
git issue #717 - avoid putting monsters on scare \
authorPatR <rankin@nethack.org>
Fri, 1 Apr 2022 12:09:58 +0000 (05:09 -0700)
committerPatR <rankin@nethack.org>
Fri, 1 Apr 2022 12:09:58 +0000 (05:09 -0700)
monster and Elbereth unless there's no other choice.

Suggested by NetSysFire, don't create new monsters on top of scrolls
of scare monster.  Not mentioned in the suggestion:  unless they are
a type of monster that isn't affected by such scrolls.  This extends
it to teleport destination too.

Avoid placing a monster on a scroll of scare monster or on engraved
Elbereth if there are other locations available.  Only performed for
callers of goodpos() who explicitly request it, which at the moment
are makemon(), rloc(), and enexto().

Also, propagate 'mmflags_nht' to a bunch of places that were left
using long or unsigned for makemon() and goodpos() flags.  I didn't
attempt to be systematic about that though.

Implements #717

doc/fixes3-7-0.txt
include/extern.h
include/hack.h
src/makemon.c
src/teleport.c

index e6edc2760d00948650ecbf18d3a6c9e75a488a59..9f276be80108a7fa030fed24212f916f34831c25 100644 (file)
@@ -856,6 +856,9 @@ kicking a trapped chest and getting the exploding chest result destroyed items
 it was possible to destroy a Rider corpse with an exploding chest
 when teleporting, don't consider pits/spiked pits/trap doors/holes as unsafe
        destination locations if hero is levitating or flying
+try to avoid locations with engraved Elbereth or scare monster scroll when
+       creating new monsters or picking teleport destinations for monsters
+       who are susceptible to those
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
index ecb0134fb0c65017cb2b3ef74145a599f3dcdd0c..afc254024d344bb83d8712a97cad205cfc3dc317 100644 (file)
@@ -1238,7 +1238,7 @@ extern struct mextra *newmextra(void);
 extern void copy_mextra(struct monst *, struct monst *);
 extern void dealloc_mextra(struct monst *);
 extern struct monst *makemon(struct permonst *, int, int, mmflags_nht);
-extern struct monst *unmakemon(struct monst *, long);
+extern struct monst *unmakemon(struct monst *, mmflags_nht);
 extern boolean create_critters(int, struct permonst *, boolean);
 extern struct permonst *rndmonst(void);
 extern struct permonst *mkclass(char, int);
@@ -2622,9 +2622,10 @@ extern void sysopt_seduce_set(int);
 /* ### teleport.c ### */
 
 extern boolean noteleport_level(struct monst *);
-extern boolean goodpos(int, int, struct monst *, long);
+extern boolean goodpos(int, int, struct monst *, mmflags_nht);
 extern boolean enexto(coord *, xchar, xchar, struct permonst *);
-extern boolean enexto_core(coord *, xchar, xchar, struct permonst *, long);
+extern boolean enexto_core(coord *, xchar, xchar, struct permonst *,
+                           mmflags_nht);
 extern void teleds(int, int, int);
 extern boolean safe_teleds(int);
 extern boolean teleport_pet(struct monst *, boolean);
index 3f5c034a7f5ab3d4ac9ed8619b05bcfbbb440a97..38c436e905caa732589cfa806de9ae3e205ee93c 100644 (file)
@@ -295,11 +295,12 @@ typedef uint32_t mmflags_nht;     /* makemon MM_ flags */
 #define MM_FEMALE   0x020000L /* female variation */
 #define MM_NOMSG    0x040000L /* no appear message */
 /* if more MM_ flag masks are added, skip or renumber the GP_ one(s) */
-#define GP_ALLOW_XY 0x080000L /* [actually used by enexto() to decide whether
-                               * to make an extra call to goodpos()]        */
-#define GP_ALLOW_U  0x100000L /* don't reject hero's location */
-#define MM_NOEXCLAM 0x200000L /* more sedate "<mon> appears." mesg for ^G */
-#define MM_IGNORELAVA   0x400000L /* ignore lava when positioning */
+#define GP_ALLOW_XY   0x080000L /* [actually used by enexto() to decide
+                                 * whether to make extra call to goodpos()] */
+#define GP_ALLOW_U    0x100000L /* don't reject hero's location */
+#define GP_CHECKSCARY 0x200000L /* check monster for onscary() */
+#define MM_NOEXCLAM   0x400000L /* more sedate "<mon> appears." mesg for ^G */
+#define MM_IGNORELAVA 0x800000L /* ignore lava when positioning */
 
 /* flags for make_corpse() and mkcorpstat(); 0..7 are recorded in obj->spe */
 #define CORPSTAT_NONE     0x00
index 6cf8205f3593f42bf2adc01f920bfbe63cb79e68..691476a6bcef0e02e765b5806bcedcfe8b2c24f7 100644 (file)
@@ -22,7 +22,7 @@ static void m_initgrp(struct monst *, int, int, int, mmflags_nht);
 static void m_initthrow(struct monst *, int, int);
 static void m_initweap(struct monst *);
 static void m_initinv(struct monst *);
-static boolean makemon_rnd_goodpos(struct monst *, long, coord *);
+static boolean makemon_rnd_goodpos(struct monst *, mmflags_nht, coord *);
 
 #define m_initsgrp(mtmp, x, y, mmf) m_initgrp(mtmp, x, y, 3, mmf)
 #define m_initlgrp(mtmp, x, y, mmf) m_initgrp(mtmp, x, y, 10, mmf)
@@ -1042,13 +1042,16 @@ newmextra(void)
 {
     struct mextra *mextra;
 
-    mextra = (struct mextra *) alloc(sizeof(struct mextra));
+    mextra = (struct mextra *) alloc(sizeof (struct mextra));
     init_mextra(mextra);
     return mextra;
 }
 
 static boolean
-makemon_rnd_goodpos(struct monst *mon, long gpflags, coord *cc)
+makemon_rnd_goodpos(
+    struct monst *mon,
+    mmflags_nht gpflags,
+    coord *cc) /* output */
 {
     int tryct = 0;
     int nx, ny;
@@ -1058,7 +1061,7 @@ makemon_rnd_goodpos(struct monst *mon, long gpflags, coord *cc)
         nx = rn1(COLNO - 3, 2);
         ny = rn2(ROWNO);
         good = (!g.in_mklev && cansee(nx,ny)) ? FALSE
-                                            : goodpos(nx, ny, mon, gpflags);
+                                              : goodpos(nx, ny, mon, gpflags);
     } while ((++tryct < 50) && !good);
 
     if (!good) {
@@ -1071,6 +1074,8 @@ makemon_rnd_goodpos(struct monst *mon, long gpflags, coord *cc)
         int bl = (g.in_mklev || Blind) ? 1 : 0;
 
         for ( ; bl < 2; bl++) {
+            if (!bl)
+                gpflags &= ~GP_CHECKSCARY; /* perhaps should be a 3rd pass */
             for (dx = 0; dx < COLNO; dx++)
                 for (dy = 0; dy < ROWNO; dy++) {
                     nx = ((dx + xofs) % (COLNO - 1)) + 1;
@@ -1113,8 +1118,10 @@ makemon_rnd_goodpos(struct monst *mon, long gpflags, coord *cc)
  *      In case we make a monster group, only return the one at [x,y].
  */
 struct monst *
-makemon(register struct permonst *ptr,
-        register int x, register int y, mmflags_nht mmflags)
+makemon(
+    struct permonst *ptr,
+    int x, int y,
+    mmflags_nht mmflags)
 {
     register struct monst *mtmp;
     struct monst fakemon;
@@ -1125,7 +1132,8 @@ makemon(register struct permonst *ptr,
             allow_minvent = ((mmflags & NO_MINVENT) == 0),
             countbirth = ((mmflags & MM_NOCOUNTBIRTH) == 0),
             allowtail = ((mmflags & MM_NOTAIL) == 0);
-    unsigned gpflags = (mmflags & MM_IGNOREWATER) ? MM_IGNOREWATER : 0;
+    mmflags_nht gpflags = (((mmflags & MM_IGNOREWATER) ? MM_IGNOREWATER : 0)
+                           | GP_CHECKSCARY);
 
     fakemon = cg.zeromonst;
     cc.x = cc.y = 0;
@@ -1142,7 +1150,8 @@ makemon(register struct permonst *ptr,
         x = cc.x;
         y = cc.y;
     } else if (byyou && !g.in_mklev) {
-        if (!enexto_core(&cc, u.ux, u.uy, ptr, gpflags))
+        if (!enexto_core(&cc, u.ux, u.uy, ptr, gpflags)
+            && !enexto_core(&cc, u.ux, u.uy, ptr, gpflags & ~GP_CHECKSCARY))
             return (struct monst *) 0;
         x = cc.x;
         y = cc.y;
@@ -1447,7 +1456,9 @@ makemon(register struct permonst *ptr,
 
 /* caller rejects makemon()'s result; always returns Null */
 struct monst *
-unmakemon(struct monst *mon, long mmflags)
+unmakemon(
+    struct monst *mon,
+    mmflags_nht mmflags)
 {
     boolean countbirth = ((mmflags & MM_NOCOUNTBIRTH) == 0);
     int mndx = monsndx(mon->data);
index 83652908e6998f23f4069dd06e5eb02eb15ce1bf..2d3ffbc5ed11be47596aba57b01c2613eba5ae5d 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "hack.h"
 
+static boolean goodpos_onscary(int, int, struct permonst *);
 static boolean tele_jump_ok(int, int, int, int);
 static boolean teleok(int, int, boolean);
 static void vault_tele(void);
@@ -31,6 +32,33 @@ noteleport_level(struct monst* mon)
     return FALSE;
 }
 
+/* this is an approximation of onscary() that doesn't use any 'struct monst'
+   fields aside from 'monst->data' */
+static boolean
+goodpos_onscary(
+    int x, int y,
+    struct permonst *mptr)
+{
+    /* onscary() checks Angels and lawful minions; this oversimplifies */
+    if (mptr->mlet == S_HUMAN || mptr->mlet == S_ANGEL
+        || is_rider(mptr) || unique_corpstat(mptr))
+        return FALSE;
+    /* onscary() checks for vampshifted vampire bats/fog clouds/wolves too */
+    if (IS_ALTAR(levl[x][y].typ) && mptr->mlet == S_VAMPIRE)
+        return TRUE;
+    /* scare monster scroll doesn't have any of the below restrictions,
+       being its own source of power */
+    if (sobj_at(SCR_SCARE_MONSTER, x, y))
+        return TRUE;
+    /* 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 */
+    if (mptr == &mons[PM_MINOTAUR] || !haseyes(mptr))
+        return FALSE;
+    return sengr_at("Elbereth", x, y, TRUE);
+}
+
 /*
  * Is (x,y) a good position of mtmp?  If mtmp is NULL, then is (x,y) good
  * for an object?
@@ -39,11 +67,15 @@ noteleport_level(struct monst* mon)
  * call it to generate new monster positions with fake monster structures.
  */
 boolean
-goodpos(int x, int y, struct monst* mtmp, long gpflags)
+goodpos(
+    int x, int y,
+    struct monst *mtmp,
+    mmflags_nht gpflags)
 {
     struct permonst *mdat = (struct permonst *) 0;
     boolean ignorewater = ((gpflags & MM_IGNOREWATER) != 0),
             ignorelava = ((gpflags & MM_IGNORELAVA) != 0),
+            checkscary = ((gpflags & GP_CHECKSCARY) != 0),
             allow_u = ((gpflags & GP_ALLOW_U) != 0);
 
     if (!isok(x, y))
@@ -113,15 +145,20 @@ goodpos(int x, int y, struct monst* mtmp, long gpflags)
             return TRUE;
         if (amorphous(mdat) && closed_door(x, y))
             return TRUE;
+        /* avoid onscary() if caller has specified that restriction */
+        if (checkscary && (mtmp->m_id ? onscary(x, y, mtmp)
+                                      : goodpos_onscary(x, y, mdat)))
+            return FALSE;
     }
     if (!accessible(x, y)) {
         if (!(is_pool(x, y) && ignorewater)
             && !(is_lava(x, y) && ignorelava))
             return FALSE;
     }
-
+    /* skip boulder locations for most creatures */
     if (sobj_at(BOULDER, x, y) && (!mdat || !throws_rocks(mdat)))
         return FALSE;
+
     return TRUE;
 }
 
@@ -136,20 +173,19 @@ goodpos(int x, int y, struct monst* mtmp, long gpflags)
 boolean
 enexto(
     coord *cc,
-    register xchar xx,
-    register xchar yy,
+    xchar xx, xchar yy,
     struct permonst *mdat)
 {
-    return enexto_core(cc, xx, yy, mdat, NO_MM_FLAGS);
+    return (enexto_core(cc, xx, yy, mdat, GP_CHECKSCARY)
+            || enexto_core(cc, xx, yy, mdat, NO_MM_FLAGS));
 }
 
 boolean
 enexto_core(
     coord *cc,
-    xchar xx,
-    xchar yy,
+    xchar xx, xchar yy,
     struct permonst *mdat,
-    long entflags)
+    mmflags_nht entflags)
 {
 #define MAX_GOOD 15
     coord good[MAX_GOOD], *good_ptr;
@@ -1156,13 +1192,12 @@ level_tele_trap(struct trap* trap, unsigned int trflags)
 /* check whether monster can arrive at location <x,y> via Tport (or fall) */
 static boolean
 rloc_pos_ok(
-    register int x,
-    register int y, /* x,y - coordinates of candidate location */
+    int x, int y, /* coordinates of candidate location */
     struct monst *mtmp)
 {
     register int xx, yy;
 
-    if (!goodpos(x, y, mtmp, 0))
+    if (!goodpos(x, y, mtmp, GP_CHECKSCARY))
         return FALSE;
     /*
      * Check for restricted areas present in some special levels.
@@ -1223,9 +1258,10 @@ rloc_pos_ok(
  * placed randomly around the head of the worm.
  */
 static void
-rloc_to_core(struct monst* mtmp,
-             int x, int y,
-             unsigned int rlocflags)
+rloc_to_core(
+    struct monst* mtmp,
+    int x, int y,
+    unsigned rlocflags)
 {
     register int oldx = mtmp->mx, oldy = mtmp->my;
     boolean resident_shk = mtmp->isshk && inhishop(mtmp);
@@ -1359,7 +1395,7 @@ rloc(
         /* if the wiz teleports away to heal, try the up staircase,
            to block the player's escaping before he's healed
            (deliberately use `goodpos' rather than `rloc_pos_ok' here) */
-        if (goodpos(x, y, mtmp, 0))
+        if (goodpos(x, y, mtmp, NO_MM_FLAGS))
             goto found_xy;
     }
 
@@ -1367,15 +1403,16 @@ rloc(
     do {
         x = rn1(COLNO - 3, 2);
         y = rn2(ROWNO);
+        /* rloc_pos_ok() passes GP_CHECKSCARY to goodpos(), we don't */
         if ((trycount < 500) ? rloc_pos_ok(x, y, mtmp)
-                             : goodpos(x, y, mtmp, 0))
+                             : goodpos(x, y, mtmp, NO_MM_FLAGS))
             goto found_xy;
     } while (++trycount < 1000);
 
     /* last ditch attempt to find a good place */
     for (x = 2; x < COLNO - 1; x++)
         for (y = 0; y < ROWNO; y++)
-            if (goodpos(x, y, mtmp, 0))
+            if (goodpos(x, y, mtmp, NO_MM_FLAGS))
                 goto found_xy;
 
     /* level either full of monsters or somehow faulty */