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 uhitm.c $NHDT-Date: 1548125661 2019/01/22 02:54:21 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.202 $ */
/* NetHack 3.6 uhitm.c $NHDT-Date: 1548209742 2019/01/23 02:15:42 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.203 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -392,6 +392,7 @@ register struct monst *mtmp;
it uses bhitpos instead; it might map an invisible monster there */
bhitpos.x = u.ux + u.dx;
bhitpos.y = u.uy + u.dy;
notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
if (attack_checks(mtmp, uwep))
return TRUE;
@@ -686,6 +687,7 @@ int dieroll;
/* not grapnels; applied implies uwep */
|| (thrown == HMON_APPLIED && is_pole(uwep)));
int jousting = 0;
long silverhit = 0L;
int wtype;
struct obj *monwep;
char unconventional[BUFSZ]; /* substituted for word "attack" in msg */
@@ -703,23 +705,15 @@ int dieroll;
else
tmp = rnd(2);
valid_weapon_attack = (tmp > 1);
/* blessed gloves give bonuses when fighting 'bare-handed' */
if (uarmg && uarmg->blessed
&& (is_undead(mdat) || is_demon(mdat) || is_vampshifter(mon)))
tmp += rnd(4);
/* So do silver rings. Note: rings are worn under gloves, so you
* don't get both bonuses.
*/
if (!uarmg) {
if (uleft && objects[uleft->otyp].oc_material == SILVER)
barehand_silver_rings++;
if (uright && objects[uright->otyp].oc_material == SILVER)
barehand_silver_rings++;
if (barehand_silver_rings && mon_hates_silver(mon)) {
tmp += rnd(20);
silvermsg = TRUE;
}
}
/* Blessed gloves give bonuses when fighting 'bare-handed'. So do
silver rings. Note: rings are worn under gloves, so you don't
get both bonuses, and two silver rings don't give double bonus. */
tmp += special_dmgval(&youmonst, mon, (W_ARMG | W_RINGL | W_RINGR),
&silverhit);
barehand_silver_rings += (((silverhit & W_RINGL) ? 1 : 0)
+ ((silverhit & W_RINGR) ? 1 : 0));
if (barehand_silver_rings > 0)
silvermsg = TRUE;
} else {
if (!(artifact_light(obj) && obj->lamplit))
Strcpy(saved_oname, cxname(obj));
@@ -1571,9 +1565,10 @@ struct attack *mattk;
}
int
damageum(mdef, mattk)
damageum(mdef, mattk, specialdmg)
register struct monst *mdef;
register struct attack *mattk;
int specialdmg; /* blessed and/or silver bonus against various things */
{
register struct permonst *pd = mdef->data;
int armpro, tmp = d((int) mattk->damn, (int) mattk->damd);
@@ -1609,19 +1604,24 @@ register struct attack *mattk;
case AD_HEAL: /* likewise */
case AD_PHYS:
physical:
if (pd == &mons[PM_SHADE]) {
tmp = 0;
if (!specialdmg)
impossible("bad shade attack function flow?");
}
tmp += specialdmg;
if (mattk->aatyp == AT_WEAP) {
if (uwep)
tmp = 0;
} else if (mattk->aatyp == AT_KICK) {
/* hmonas() uses known_hitum() to deal physical damage,
then also damageum() for non-AD_PHYS; don't inflict
extra physical damage for unusual damage types */
tmp = 0;
} else if (mattk->aatyp == AT_KICK
|| mattk->aatyp == AT_CLAW
|| mattk->aatyp == AT_TUCH
|| mattk->aatyp == AT_HUGS) {
if (thick_skinned(pd))
tmp = 0;
if (pd == &mons[PM_SHADE]) {
if (!(uarmf && uarmf->blessed)) {
impossible("bad shade attack function flow?");
tmp = 0;
} else
tmp = rnd(4); /* bless damage */
}
tmp = (mattk->aatyp == AT_KICK) ? 0 : (tmp + 1) / 2;
/* add ring(s) of increase damage */
if (u.udaminc > 0) {
/* applies even if damage was 0 */
@@ -2122,7 +2122,7 @@ register struct attack *mattk;
}
/* Use up amulet of life saving */
if (!!(otmp = mlifesaver(mdef)))
if ((otmp = mlifesaver(mdef)) != 0)
m_useup(mdef, otmp);
newuhs(FALSE);
@@ -2284,9 +2284,20 @@ register struct monst *mon;
{
struct attack *mattk, alt_attk;
struct obj *weapon, **originalweapon;
boolean altwep = FALSE, weapon_used = FALSE;
boolean altwep = FALSE, weapon_used = FALSE, odd_claw = TRUE;
int i, tmp, armorpenalty, sum[NATTK], nsum = 0, dhit = 0, attknum = 0;
int dieroll;
int dieroll, multi_claw = 0;
/* with just one touch/claw/weapon attack, both rings matter;
with more than one, alternate right and left when checking
whether silver ring causes successful hit */
for (i = 0; i < NATTK; i++) {
mattk = getmattk(&youmonst, mon, i, sum, &alt_attk);
if (mattk->aatyp == AT_WEAP
|| mattk->aatyp == AT_CLAW || mattk->aatyp == AT_TUCH)
++multi_claw;
}
multi_claw = (multi_claw > 1); /* switch from count to yes/no */
for (i = 0; i < NATTK; i++) {
sum[i] = 0;
@@ -2294,7 +2305,9 @@ register struct monst *mon;
weapon = 0;
switch (mattk->aatyp) {
case AT_WEAP:
/* if (!uwep) goto weaponless; */
use_weapon:
odd_claw = !odd_claw; /* see case AT_CLAW,AT_TUCH below */
/* if we've already hit with a two-handed weapon, we don't
get to make another weapon attack (note: monsters who
use weapons do not have this restriction, but they also
@@ -2363,7 +2376,7 @@ register struct monst *mon;
}
/* Do not print "You hit" message; known_hitum already did it. */
if (dhit && mattk->adtyp != AD_SPEL && mattk->adtyp != AD_PHYS)
sum[i] = damageum(mon, mattk);
sum[i] = damageum(mon, mattk, 0);
break;
case AT_CLAW:
if (uwep && !cantwield(youmonst.data) && !weapon_used)
@@ -2378,73 +2391,189 @@ register struct monst *mon;
case AT_STNG:
case AT_BUTT:
case AT_TENT:
/*weaponless:*/
tmp = find_roll_to_hit(mon, mattk->aatyp, (struct obj *) 0,
&attknum, &armorpenalty);
dieroll = rnd(20);
dhit = (tmp > dieroll || u.uswallow);
if (dhit) {
int compat;
int compat, specialdmg;
long silverhit = 0L;
const char *verb = 0; /* verb or body part */
if (!u.uswallow
&& (compat = could_seduce(&youmonst, mon, mattk))) {
&& (compat = could_seduce(&youmonst, mon, mattk)) != 0) {
You("%s %s %s.",
mon->mcansee && haseyes(mon->data) ? "smile at"
: "talk to",
(mon->mcansee && haseyes(mon->data)) ? "smile at"
: "talk to",
mon_nam(mon),
compat == 2 ? "engagingly" : "seductively");
(compat == 2) ? "engagingly" : "seductively");
/* doesn't anger it; no wakeup() */
sum[i] = damageum(mon, mattk);
sum[i] = damageum(mon, mattk, 0);
break;
}
wakeup(mon, TRUE);
/* maybe this check should be in damageum()? */
if (mon->data == &mons[PM_SHADE]
&& !(mattk->aatyp == AT_KICK && uarmf
&& uarmf->blessed)) {
Your("attack passes harmlessly through %s.",
mon_nam(mon));
specialdmg = 0; /* blessed and/or silver bonus */
switch (mattk->aatyp) {
case AT_CLAW:
case AT_TUCH:
/* verb=="claws" may be overridden below */
verb = (mattk->aatyp == AT_TUCH) ? "touch" : "claws";
/* decide if silver-hater will be hit by silver ring(s);
for 'multi_claw' where attacks alternate right/left,
assume 'even' claw or touch attacks use right hand
or paw, 'odd' ones use left for ring interaction;
even vs odd is based on actual attacks rather
than on index into mon->dat->mattk[] so that {bite,
claw,claw} instead of {claw,claw,bite} doesn't
make poly'd hero mysteriously become left-handed */
odd_claw = !odd_claw;
specialdmg = special_dmgval(&youmonst, mon,
W_ARMG
| ((odd_claw || !multi_claw)
? W_RINGL : 0L)
| ((!odd_claw || !multi_claw)
? W_RINGR : 0L),
&silverhit);
break;
case AT_TENT:
/* assumes mind flayer's tentacles-on-head rather
than sea monster's tentacle-as-arm */
verb = "tentacles";
break;
case AT_KICK:
verb = "kick";
specialdmg = special_dmgval(&youmonst, mon, W_ARMF,
&silverhit);
break;
case AT_BUTT:
verb = "head butt"; /* mbodypart(mon,HEAD)=="head" */
/* hypothetical; if any form with a head-butt attack
could wear a helmet, it would hit shades when
wearing a blessed (or silver) one */
specialdmg = special_dmgval(&youmonst, mon, W_ARMH,
&silverhit);
break;
case AT_BITE:
verb = "bite";
break;
case AT_STNG:
verb = "sting";
break;
default:
verb = "hit";
break;
}
if (mattk->aatyp == AT_KICK)
You("kick %s.", mon_nam(mon));
else if (mattk->aatyp == AT_BITE)
You("bite %s.", mon_nam(mon));
else if (mattk->aatyp == AT_STNG)
You("sting %s.", mon_nam(mon));
else if (mattk->aatyp == AT_BUTT)
You("butt %s.", mon_nam(mon));
else if (mattk->aatyp == AT_TUCH)
You("touch %s.", mon_nam(mon));
else if (mattk->aatyp == AT_TENT)
Your("tentacles suck %s.", mon_nam(mon));
else
You("hit %s.", mon_nam(mon));
sum[i] = damageum(mon, mattk);
} else {
if (mon->data == &mons[PM_SHADE] && !specialdmg) {
if (!strcmp(verb, "hit")
|| (mattk->aatyp == AT_CLAW && humanoid(mon->data)))
verb = "attack";
Your("%s %s harmlessly through %s.",
verb, vtense(verb, "pass"), mon_nam(mon));
} else {
if (mattk->aatyp == AT_TENT) {
Your("tentacles suck %s.", mon_nam(mon));
} else {
if (mattk->aatyp == AT_CLAW)
verb = "hit"; /* not "claws" */
You("%s %s.", verb, mon_nam(mon));
if (silverhit && flags.verbose)
silver_sears(&youmonst, mon, silverhit);
}
sum[i] = damageum(mon, mattk, specialdmg);
}
} else { /* !dhit */
missum(mon, mattk, (tmp + armorpenalty > dieroll));
}
break;
case AT_HUGS:
case AT_HUGS: {
int specialdmg;
long silverhit = 0L;
boolean byhand = hug_throttles(&mons[u.umonnum]), /* rope golem */
unconcerned = (byhand && !can_be_strangled(mon));
if (sticks(mon->data) || u.uswallow || notonhead
|| (byhand && (uwep || !has_head(mon->data)))) {
/* can't hold a holder due to subsequent ambiguity over
who is holding whom; can't hug engulfer from inside;
can't hug a worm tail (would immobilize entire worm!);
byhand: can't choke something that lacks a head;
not allowed to make a choking hug if wielding a weapon
(but might have grabbed w/o weapon, then wielded one,
and may even be attacking a different monster now) */
if (byhand && uwep && u.ustuck
&& !(sticks(u.ustuck->data) || u.uswallow))
uunstick();
continue; /* not 'break'; bypass passive counter-attack */
}
/* automatic if prev two attacks succeed, or if
* already grabbed in a previous attack
*/
already grabbed in a previous attack */
dhit = 1;
wakeup(mon, TRUE);
if (mon->data == &mons[PM_SHADE])
Your("hug passes harmlessly through %s.", mon_nam(mon));
else if (!sticks(mon->data) && !u.uswallow) {
if (mon == u.ustuck) {
pline("%s is being %s.", Monnam(mon),
u.umonnum == PM_ROPE_GOLEM ? "choked" : "crushed");
sum[i] = damageum(mon, mattk);
} else if (i >= 2 && sum[i - 1] && sum[i - 2]) {
You("grab %s!", mon_nam(mon));
u.ustuck = mon;
sum[i] = damageum(mon, mattk);
/* choking hug/throttling grab uses hands (gloves or rings);
normal hug uses outermost of cloak/suit/shirt */
specialdmg = special_dmgval(&youmonst, mon,
byhand ? (W_ARMG | W_RINGL | W_RINGR)
: (W_ARMC | W_ARM | W_ARMU),
&silverhit);
if (unconcerned) {
/* strangling something which can't be strangled */
if (mattk != &alt_attk) {
alt_attk = *mattk;
mattk = &alt_attk;
}
/* change damage to 1d1; not strangling but still
doing [minimal] physical damage to victim's body */
mattk->damn = mattk->damd = 1;
/* don't give 'unconcerned' feedback if there is extra damage
or if it is nearly destroyed or if creature doesn't have
the mental ability to be concerned in the first place */
if (specialdmg || mindless(mon->data)
|| mon->mhp <= 1 + max(u.udaminc, 1))
unconcerned = FALSE;
}
break;
if (mon->data == &mons[PM_SHADE]) {
const char *verb = byhand ? "grasp" : "hug";
/* hugging a shade; successful if blessed outermost armor
for normal hug, or blessed gloves or silver ring(s) for
choking hug; deals damage but never grabs hold */
if (specialdmg) {
You("%s %s%s", verb, mon_nam(mon), exclam(specialdmg));
if (silverhit && flags.verbose)
silver_sears(&youmonst, mon, silverhit);
sum[i] = damageum(mon, mattk, specialdmg);
} else {
Your("%s passes harmlessly through %s.",
verb, mon_nam(mon));
}
break;
}
/* hug attack against ordinary foe */
if (mon == u.ustuck) {
pline("%s is being %s%s.", Monnam(mon),
byhand ? "throttled" : "crushed",
/* extra feedback for non-breather being choked */
unconcerned ? " but doesn't seem concerned" : "");
if (silverhit && flags.verbose)
silver_sears(&youmonst, mon, silverhit);
sum[i] = damageum(mon, mattk, specialdmg);
} else if (i >= 2 && sum[i - 1] && sum[i - 2]) {
/* in case we're hugging a new target while already
holding something else; yields feedback
"<u.ustuck> is no longer in your clutches" */
if (u.ustuck && u.ustuck != mon)
uunstick();
You("grab %s!", mon_nam(mon));
u.ustuck = mon;
if (silverhit && flags.verbose)
silver_sears(&youmonst, mon, silverhit);
sum[i] = damageum(mon, mattk, specialdmg);
}
break; /* AT_HUGS */
}
case AT_EXPL: /* automatic hit if next to */
dhit = -1;