fix #H6422 - hmonas against shades

I did much of this quite some time ago, as prequisite for a different
bug report about monsters vs shades, then set it aside.  It ended up
being more complicated than I anticipated.

When deciding whether various non-weapon attacks might hit a shade,
hmonas() was not checking for blessed or silver armor that should have
been applicable.  It did check boots when kicking, but not gloves or
rings (when no gloves) when touching, or outermost of cloak/suit/shirt
when hugging, or helmet when head-butting.  (The last one is actually
moot because nothing with a head-butt attack is able to wear a helm.)

The problem was more general than just whether attacks might hit and
hurt shades.  Various undead and/or demons are also affected by blessed
and/or silver attack but weren't for non-weapon attacks by poly'd hero.

At least two unrelated bugs are fixed:  a rope golem's AT_HUGS attack
gives feedback about choking but was fully effective against monsters
which fail the can_be_strangled() test.  And it was possible to hug a
long worm's tail, rendering the entire worm immobile.

The report also suggested that all artifacts be able to hit shades for
full effect, but by the time shades are encountered everyone has an
artifact so that would nullify a shade's most interesting ability.

TODO:  monster against hero and monster against other monster need to
have similar changes.
This commit is contained in:
PatR
2019-01-22 18:15:49 -08:00
parent d0cc645961
commit deed117e7f
6 changed files with 347 additions and 100 deletions

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 weapon.c $NHDT-Date: 1547025169 2019/01/09 09:12:49 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.68 $ */
/* NetHack 3.6 weapon.c $NHDT-Date: 1548209744 2019/01/23 02:15:44 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.69 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
@@ -350,6 +350,117 @@ struct monst *mon;
return tmp;
}
/* check whether blessed and/or silver damage applies for *non-weapon* hit;
return value is the amount of the extra damage */
int
special_dmgval(magr, mdef, armask, silverhit_p)
struct monst *magr, *mdef;
long armask; /* armor mask, multiple bits accepted for W_ARMC|W_ARM|W_ARMU
* or W_ARMG|W_RINGL|W_RINGR only */
long *silverhit_p; /* output flag mask for silver bonus */
{
struct obj *obj;
struct permonst *ptr = mdef->data;
boolean left_ring = (armask & W_RINGL) ? TRUE : FALSE,
right_ring = (armask & W_RINGR) ? TRUE : FALSE;
long silverhit = 0L;
int bonus = 0;
obj = 0;
if (armask & (W_ARMC | W_ARM | W_ARMU)) {
if ((armask & W_ARMC) != 0L
&& (obj = which_armor(magr, W_ARMC)) != 0)
armask = W_ARMC;
else if ((armask & W_ARM) != 0L
&& (obj = which_armor(magr, W_ARM)) != 0)
armask = W_ARM;
else if ((armask & W_ARMU) != 0L
&& (obj = which_armor(magr, W_ARMU)) != 0)
armask = W_ARMU;
else
armask = 0L;
} else if (armask & (W_ARMG | W_RINGL | W_RINGR)) {
armask = ((obj = which_armor(magr, W_ARMG)) != 0) ? W_ARMG : 0L;
} else {
obj = which_armor(magr, armask);
}
if (obj) {
if (obj->blessed
&& (is_undead(ptr) || is_demon(ptr) || is_vampshifter(mdef)))
bonus += rnd(4);
/* the only silver armor is shield of reflection (silver dragon
scales refer to color, not material) and the only way to hit
with one--aside from throwing--is to wield it and perform a
weapon hit, but we include a general check here */
if (objects[obj->otyp].oc_material == SILVER
&& mon_hates_silver(mdef)) {
bonus += rnd(20);
silverhit |= armask;
}
/* when no gloves we check for silver rings (blessed rings ignored) */
} else if ((left_ring || right_ring) && magr == &youmonst) {
if (left_ring && uleft) {
if (objects[uleft->otyp].oc_material == SILVER
&& mon_hates_silver(mdef)) {
bonus += rnd(20);
silverhit |= W_RINGL;
}
}
if (right_ring && uright) {
if (objects[uright->otyp].oc_material == SILVER
&& mon_hates_silver(mdef)) {
/* two silver rings don't give double silver damage
but 'silverhit' messages might be adjusted for them */
if (!(silverhit & W_RINGL))
bonus += rnd(20);
silverhit |= W_RINGR;
}
}
}
if (silverhit_p)
*silverhit_p = silverhit;
return bonus;
}
/* give a "silver <item> sears <target>" message;
not used for weapon hit, so we only handle rings */
void
silver_sears(magr, mdef, silverhit)
struct monst *magr UNUSED;
struct monst *mdef;
long silverhit;
{
char rings[20]; /* plenty of room for "rings" */
int ltyp = ((uleft && (silverhit & W_RINGL) != 0L)
? uleft->otyp : STRANGE_OBJECT),
rtyp = ((uright && (silverhit & W_RINGR) != 0L)
? uright->otyp : STRANGE_OBJECT);
boolean both,
l_ag = (objects[ltyp].oc_material == SILVER && uleft->dknown),
r_ag = (objects[rtyp].oc_material == SILVER && uright->dknown);
if ((silverhit & (W_RINGL | W_RINGR)) != 0L) {
/* plural if both the same type (so not multi_claw and both rings
are non-Null) and either both known or neither known, or both
silver (in case there is ever more than one type of silver ring)
and both known; singular if multi_claw (where one of ltyp or
rtyp will always be STRANGE_OBJECT) even if both rings are known
silver [see hmonas(uhitm.c) for explanation of 'multi_claw'] */
both = ((ltyp == rtyp && uleft->dknown == uright->dknown)
|| (l_ag && r_ag));
Sprintf(rings, "ring%s", both ? "s" : "");
Your("%s%s %s %s!",
(l_ag || r_ag) ? "silver "
: both ? ""
: ((silverhit & W_RINGL) != 0L) ? "left "
: "right ",
rings, vtense(rings, "sear"), mon_nam(mdef));
}
}
STATIC_DCL struct obj *FDECL(oselect, (struct monst *, int));
#define Oselect(x) \
if ((otmp = oselect(mtmp, x)) != 0) \