From deed117e7f86d667c48367c82e9572753f2be31d Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 22 Jan 2019 18:15:49 -0800 Subject: [PATCH] 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. --- doc/fixes36.2 | 7 +- include/extern.h | 6 +- include/mondata.h | 3 +- src/dokick.c | 35 +++--- src/uhitm.c | 283 +++++++++++++++++++++++++++++++++------------- src/weapon.c | 113 +++++++++++++++++- 6 files changed, 347 insertions(+), 100 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 4028cda99..d5ba86533 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.234 $ $NHDT-Date: 1548208486 2019/01/23 01:54:46 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.235 $ $NHDT-Date: 1548209734 2019/01/23 02:15:34 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -345,6 +345,11 @@ the simulation of dual weapon combat when polymorphed into a form with more demons gated in other demons without any message if vampire revives from from dead bat/cloud/wolf shape held by hero poly'd into grabber, release it from hero's grasp +various non-weapon attacks while polymorphed didn't inflict blessed or silver + weapon damage when worn items contacted vulnerable target monsters +hero poly'd into rope golem form could choke headless or non-breathing monsts +hero poly'd into creature with hug attack could hug a long worm's tail which + rendered the whole worm immobile Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 019cdaa16..4f63d9963 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1548208224 2019/01/23 01:50:24 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.683 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1548209735 2019/01/23 02:15:35 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.684 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2516,7 +2516,7 @@ E int FDECL(find_roll_to_hit, (struct monst *, UCHAR_P, struct obj *, int *, int *)); E boolean FDECL(attack, (struct monst *)); E boolean FDECL(hmon, (struct monst *, struct obj *, int, int)); -E int FDECL(damageum, (struct monst *, struct attack *)); +E int FDECL(damageum, (struct monst *, struct attack *, int)); E void FDECL(missum, (struct monst *, struct attack *, BOOLEAN_P)); E int FDECL(passive, (struct monst *, struct obj *, BOOLEAN_P, int, UCHAR_P, BOOLEAN_P)); @@ -2733,6 +2733,8 @@ E int FDECL(vms_get_saved_games, (const char *, char ***)); E const char *FDECL(weapon_descr, (struct obj *)); E int FDECL(hitval, (struct obj *, struct monst *)); E int FDECL(dmgval, (struct obj *, struct monst *)); +E int FDECL(special_dmgval, (struct monst *, struct monst *, long, long *)); +E void FDECL(silver_sears, (struct monst *, struct monst *, long)); E struct obj *FDECL(select_rwep, (struct monst *)); E boolean FDECL(monmightthrowwep, (struct obj *)); E struct obj *FDECL(select_hwep, (struct monst *)); diff --git a/include/mondata.h b/include/mondata.h index 8febb8fb2..dc7fba275 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 mondata.h $NHDT-Date: 1547086248 2019/01/10 02:10:48 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.35 $ */ +/* NetHack 3.6 mondata.h $NHDT-Date: 1548209737 2019/01/23 02:15:37 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.36 $ */ /* Copyright (c) 1989 Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ @@ -59,6 +59,7 @@ #define slithy(ptr) (((ptr)->mflags1 & M1_SLITHY) != 0L) #define is_wooden(ptr) ((ptr) == &mons[PM_WOOD_GOLEM]) #define thick_skinned(ptr) (((ptr)->mflags1 & M1_THICK_HIDE) != 0L) +#define hug_throttles(ptr) ((ptr) == &mons[PM_ROPE_GOLEM]) #define slimeproof(ptr) \ ((ptr) == &mons[PM_GREEN_SLIME] || flaming(ptr) || noncorporeal(ptr)) #define lays_eggs(ptr) (((ptr)->mflags1 & M1_OVIPAROUS) != 0L) diff --git a/src/dokick.c b/src/dokick.c index 04d33e3e2..57bde5b74 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 dokick.c $NHDT-Date: 1547086527 2019/01/10 02:15:27 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.129 $ */ +/* NetHack 3.6 dokick.c $NHDT-Date: 1548209738 2019/01/23 02:15:38 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.130 $ */ /* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -28,15 +28,15 @@ STATIC_DCL void FDECL(drop_to, (coord *, SCHAR_P)); static const char kick_passes_thru[] = "kick passes harmlessly through"; +/* kicking damage when not poly'd into a form with a kick attack */ STATIC_OVL void kickdmg(mon, clumsy) -register struct monst *mon; -register boolean clumsy; +struct monst *mon; +boolean clumsy; { - register int mdx, mdy; - register int dmg = (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 15; - int kick_skill = P_NONE; - int blessed_foot_damage = 0; + int mdx, mdy; + int dmg = (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 15; + int specialdmg, kick_skill = P_NONE; boolean trapkilled = FALSE; if (uarmf && uarmf->otyp == KICKING_BOOTS) @@ -50,15 +50,13 @@ register boolean clumsy; if (thick_skinned(mon->data)) dmg = 0; - /* attacking a shade is useless */ + /* attacking a shade is normally useless */ if (mon->data == &mons[PM_SHADE]) dmg = 0; - if ((is_undead(mon->data) || is_demon(mon->data) || is_vampshifter(mon)) - && uarmf && uarmf->blessed) - blessed_foot_damage = 1; + specialdmg = special_dmgval(&youmonst, mon, W_ARMF, (long *) 0); - if (mon->data == &mons[PM_SHADE] && !blessed_foot_damage) { + if (mon->data == &mons[PM_SHADE] && !specialdmg) { pline_The("%s.", kick_passes_thru); /* doesn't exercise skill or abuse alignment or frighten pet, and shades have no passive counterattack */ @@ -90,8 +88,7 @@ register boolean clumsy; /* a good kick exercises your dex */ exercise(A_DEX, TRUE); } - if (blessed_foot_damage) - dmg += rnd(4); + dmg += specialdmg; /* for blessed (or hypothetically, silver) boots */ if (uarmf) dmg += uarmf->spe; dmg += u.udaminc; /* add ring(s) of increase damage */ @@ -189,7 +186,7 @@ xchar x, y; */ if (Upolyd && attacktype(youmonst.data, AT_KICK)) { struct attack *uattk; - int sum, kickdieroll, armorpenalty, + int sum, kickdieroll, armorpenalty, specialdmg, attknum = 0, tmp = find_roll_to_hit(mon, AT_KICK, (struct obj *) 0, &attknum, &armorpenalty); @@ -205,14 +202,16 @@ xchar x, y; if (uattk->aatyp != AT_KICK) continue; - if (mon->data == &mons[PM_SHADE] && (!uarmf || !uarmf->blessed)) { + kickdieroll = rnd(20); + specialdmg = special_dmgval(&youmonst, mon, W_ARMF, (long *) 0); + if (mon->data == &mons[PM_SHADE] && !specialdmg) { /* doesn't matter whether it would have hit or missed, and shades have no passive counterattack */ Your("%s %s.", kick_passes_thru, mon_nam(mon)); break; /* skip any additional kicks */ - } else if (tmp > (kickdieroll = rnd(20))) { + } else if (tmp > kickdieroll) { You("kick %s.", mon_nam(mon)); - sum = damageum(mon, uattk); + sum = damageum(mon, uattk, specialdmg); (void) passive(mon, uarmf, (boolean) (sum > 0), (sum != 2), AT_KICK, FALSE); if (sum == 2) diff --git a/src/uhitm.c b/src/uhitm.c index 117c72de4..cefe009eb 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -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 + " 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; diff --git a/src/weapon.c b/src/weapon.c index 5ca8d7a6e..3e817a7a3 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -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 sears " 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) \ -- 2.40.0