diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index e6edc2760..9f276be80 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -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 diff --git a/include/extern.h b/include/extern.h index ecb0134fb..afc254024 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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); diff --git a/include/hack.h b/include/hack.h index 3f5c034a7..38c436e90 100644 --- a/include/hack.h +++ b/include/hack.h @@ -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 " 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 " 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 diff --git a/src/makemon.c b/src/makemon.c index 6cf8205f3..691476a6b 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -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); diff --git a/src/teleport.c b/src/teleport.c index 83652908e..2d3ffbc5e 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -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 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 */