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
This commit is contained in:
PatR
2022-10-15 02:13:39 -07:00
committed by nhmall
parent e135046497
commit 659f16070c
5 changed files with 36 additions and 19 deletions

View 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 */

View 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);

View 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;

View File

@@ -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)

View 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;
}
/*