fix #H1554, #H1736, github issue #240 - silver )

Fixes #240

Monster versus monster (melee and throwing) didn't handle shades
(need silver or blessed weapon to take damage) or silver feedback
(extra info when silver-haters are hit).

I did a lot of test, revise, re-test but didn't always re-test
everything that had previously been tested, so bugs that I thought
were quashed might have crept in.

Now if a missile weapon "passes harmlessly through the shade" it
will continue on and maybe hit something else.  (Regular misses
still stop at the missed target.)

A couple of minor ball&chain changes accidentally got included.
This commit is contained in:
PatR
2019-11-13 15:47:46 -08:00
parent 55bac90292
commit f3a923d647
8 changed files with 187 additions and 47 deletions

View File

@@ -1,4 +1,4 @@
$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.171 $ $NHDT-Date: 1573505739 2019/11/11 20:55:39 $
$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.172 $ $NHDT-Date: 1573688684 2019/11/13 23:44:44 $
This fixes36.3 file is here to capture information about updates in the 3.6.x
lineage following the release of 3.6.2 in May 2019. Please note, however,
@@ -223,6 +223,7 @@ putting on gloves while having slippery fingers transfered slipperiness to
losing them in other ways transfered slipperiness to bare fingers
when a monster reads a scroll of fire to cure sliming, don't access that
scroll's memory after it has been used up (bcsign)
monster vs monster attacks didn't handle shades or silver weapon feedback
Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 extern.h $NHDT-Date: 1573346164 2019/11/10 00:36:04 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.738 $ */
/* NetHack 3.6 extern.h $NHDT-Date: 1573688684 2019/11/13 23:44:44 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.739 $ */
/* Copyright (c) Steve Creps, 1988. */
/* NetHack may be freely redistributed. See license for details. */
@@ -2566,6 +2566,8 @@ 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 boolean FDECL(shade_miss, (struct monst *, struct monst *, struct obj *,
BOOLEAN_P, BOOLEAN_P));
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,

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 dothrow.c $NHDT-Date: 1569276989 2019/09/23 22:16:29 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.161 $ */
/* NetHack 3.6 dothrow.c $NHDT-Date: 1573688688 2019/11/13 23:44:48 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.164 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1151,6 +1151,10 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
will be left with a stale pointer. */
if (u.uswallow) {
if (obj == uball) {
uball->ox = uchain->ox = u.ux;
uball->oy = uchain->oy = u.uy;
}
mon = u.ustuck;
bhitpos.x = mon->mx;
bhitpos.y = mon->my;
@@ -1285,7 +1289,7 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
(void) snuff_candle(obj);
notonhead = (bhitpos.x != mon->mx || bhitpos.y != mon->my);
obj_gone = thitmonst(mon, obj);
/* Monster may have been tamed; this frees old mon */
/* Monster may have been tamed; this frees old mon [obsolete] */
mon = m_at(bhitpos.x, bhitpos.y);
/* [perhaps this should be moved into thitmonst or hmon] */
@@ -1310,8 +1314,8 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
clear_thrownobj = TRUE;
goto throwit_return;
} else {
/* Mjollnir must we wielded to be thrown--caller verifies this;
aklys must we wielded as primary to return when thrown */
/* Mjollnir must be wielded to be thrown--caller verifies this;
aklys must be wielded as primary to return when thrown */
if (iflags.returning_missile) { /* Mjollnir or aklys */
if (rn2(100)) {
if (tethered_weapon)

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 mhitm.c $NHDT-Date: 1560161806 2019/06/10 10:16:46 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.116 $ */
/* NetHack 3.6 mhitm.c $NHDT-Date: 1573688692 2019/11/13 23:44:52 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.117 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
@@ -63,16 +63,35 @@ struct attack *mattk;
{
const char *fmt;
char buf[BUFSZ];
boolean showit = FALSE;
/* unhiding or unmimicking happens even if hero can't see it
because the formerly concealed monster is now in action */
if (M_AP_TYPE(mdef)) {
seemimic(mdef);
showit |= vis;
} else if (mdef->mundetected) {
mdef->mundetected = 0;
showit |= vis;
}
if (M_AP_TYPE(magr)) {
seemimic(magr);
showit |= vis;
} else if (magr->mundetected) {
magr->mundetected = 0;
showit |= vis;
}
if (vis) {
if (!canspotmon(magr))
map_invisible(magr->mx, magr->my);
else if (showit)
newsym(magr->mx, magr->my);
if (!canspotmon(mdef))
map_invisible(mdef->mx, mdef->my);
if (M_AP_TYPE(mdef))
seemimic(mdef);
if (M_AP_TYPE(magr))
seemimic(magr);
else if (showit)
newsym(mdef->mx, mdef->my);
fmt = (could_seduce(magr, mdef, mattk) && !magr->mcan)
? "%s pretends to be friendly to"
: "%s misses";
@@ -512,24 +531,48 @@ hitmm(magr, mdef, mattk)
register struct monst *magr, *mdef;
struct attack *mattk;
{
boolean weaponhit = ((mattk->aatyp == AT_WEAP
|| (mattk->aatyp == AT_CLAW && otmp))),
silverhit = (weaponhit && otmp
&& objects[otmp->otyp].oc_material == SILVER),
showit = FALSE;
/* unhiding or unmimicking happens even if hero can't see it
because the formerly concealed monster is now in action */
if (M_AP_TYPE(mdef)) {
seemimic(mdef);
showit |= vis;
} else if (mdef->mundetected) {
mdef->mundetected = 0;
showit |= vis;
}
if (M_AP_TYPE(magr)) {
seemimic(magr);
showit |= vis;
} else if (magr->mundetected) {
magr->mundetected = 0;
showit |= vis;
}
if (vis) {
int compat;
char buf[BUFSZ];
if (!canspotmon(magr))
map_invisible(magr->mx, magr->my);
else if (showit)
newsym(magr->mx, magr->my);
if (!canspotmon(mdef))
map_invisible(mdef->mx, mdef->my);
if (M_AP_TYPE(mdef))
seemimic(mdef);
if (M_AP_TYPE(magr))
seemimic(magr);
else if (showit)
newsym(mdef->mx, mdef->my);
if ((compat = could_seduce(magr, mdef, mattk)) && !magr->mcan) {
Sprintf(buf, "%s %s", Monnam(magr),
mdef->mcansee ? "smiles at" : "talks to");
pline("%s %s %s.", buf, mon_nam(mdef),
compat == 2 ? "engagingly" : "seductively");
} else {
} else if (!shade_miss(magr, mdef, otmp, FALSE, TRUE)) {
char magr_name[BUFSZ];
Strcpy(magr_name, Monnam(magr));
@@ -559,6 +602,28 @@ struct attack *mattk;
Sprintf(buf, "%s hits", magr_name);
}
pline("%s %s.", buf, mon_nam_too(mdef, magr));
if (mon_hates_silver(mdef) && silverhit) {
char *mdef_name = mon_nam_too(mdef, magr);
/* note: mon_nam_too returns a modifiable buffer; so
does s_suffix, but it returns a single static buffer
and we might be calling it twice for this message */
Strcpy(magr_name, s_suffix(magr_name));
if (!noncorporeal(mdef->data) && !amorphous(mdef->data)) {
if (mdef != magr) {
mdef_name = s_suffix(mdef_name);
} else {
(void) strsubst(mdef_name, "himself", "his own");
(void) strsubst(mdef_name, "herself", "her own");
(void) strsubst(mdef_name, "itself", "its own");
}
Strcat(mdef_name, " flesh");
}
pline("%s %s sears %s!", magr_name, /*s_suffix(magr_name), */
simpleonames(otmp), mdef_name);
}
}
} else
noises(magr, mattk);
@@ -895,13 +960,21 @@ register struct attack *mattk;
physical:
if (mattk->aatyp == AT_KICK && thick_skinned(pd)) {
tmp = 0;
} else if (mattk->aatyp == AT_WEAP) {
} else if (mattk->aatyp == AT_WEAP
|| (mattk->aatyp == AT_CLAW && otmp)) {
if (mdef->data == &mons[PM_SHADE]
&& !(otmp && objects[otmp->otyp].oc_material == SILVER)) {
/* "passes harmlessly through" given by hitmm() */
tmp = 0;
break;
}
if (otmp) {
struct obj *marmg;
if (otmp->otyp == CORPSE
&& touch_petrifies(&mons[otmp->corpsenm]))
goto do_stone;
tmp += dmgval(otmp, mdef);
if ((marmg = which_armor(magr, W_ARMG)) != 0
&& marmg->otyp == GAUNTLETS_OF_POWER)

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 mhitu.c $NHDT-Date: 1562800504 2019/07/10 23:15:04 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.166 $ */
/* NetHack 3.6 mhitu.c $NHDT-Date: 1573688693 2019/11/13 23:44:53 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.167 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1793,8 +1793,7 @@ struct attack *mattk;
if (!engulf_target(mtmp, &youmonst))
return 0;
if ((t && is_pit(t->ttyp))
&& sobj_at(BOULDER, u.ux, u.uy))
if ((t && is_pit(t->ttyp)) && sobj_at(BOULDER, u.ux, u.uy))
return 0;
if (Punished)
@@ -1877,6 +1876,14 @@ struct attack *mattk;
if (mtmp != u.ustuck)
return 0;
if (Punished) {
/* ball&chain are in limbo while swallowed; update their internal
location to be at swallower's spot */
if (uchain->where == OBJ_FREE)
uchain->ox = mtmp->mx, uchain->oy = mtmp->my;
if (uball->where == OBJ_FREE)
uball->ox = mtmp->mx, uball->oy = mtmp->my;
}
if (u.uswldtim > 0)
u.uswldtim -= 1;

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 mthrowu.c $NHDT-Date: 1564767726 2019/08/02 17:42:06 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.85 $ */
/* NetHack 3.6 mthrowu.c $NHDT-Date: 1573688695 2019/11/13 23:44:55 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.86 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Pasi Kallinen, 2016. */
/* NetHack may be freely redistributed. See license for details. */
@@ -354,6 +354,10 @@ boolean verbose; /* give message(s) even when you can't see what happened */
damage = dmgval(otmp, mtmp);
if (otmp->otyp == ACID_VENOM && resists_acid(mtmp))
damage = 0;
#if 0 /* can't use this because we don't have the attacker */
if (is_orc(mtmp->data) && is_elf(?magr?))
damage++;
#endif
if (ismimic)
seemimic(mtmp);
mtmp->msleeping = 0;
@@ -384,10 +388,19 @@ boolean verbose; /* give message(s) even when you can't see what happened */
}
if (objects[otmp->otyp].oc_material == SILVER
&& mon_hates_silver(mtmp)) {
if (vis)
pline_The("silver sears %s flesh!", s_suffix(mon_nam(mtmp)));
else if (verbose && !target)
pline("Its flesh is seared!");
boolean flesh = (!noncorporeal(mtmp->data)
&& !amorphous(mtmp->data));
/* note: extra silver damage is handled by dmgval() */
if (vis) {
char *m_name = mon_nam(mtmp);
if (flesh) /* s_suffix returns a modifiable buffer */
m_name = strcat(s_suffix(m_name), " flesh");
pline_The("silver sears %s!", m_name);
} else if (verbose && !target) {
pline("%s is seared!", flesh ? "Its flesh" : "It");
}
}
if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx, mtmp->my)) {
if (resists_acid(mtmp)) {
@@ -539,7 +552,12 @@ struct obj *obj; /* missile (or stack providing it) */
while (range-- > 0) { /* Actually the loop is always exited by break */
bhitpos.x += dx;
bhitpos.y += dy;
if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
mtmp = m_at(bhitpos.x, bhitpos.y);
if (mtmp && shade_miss(mon, mtmp, singleobj, TRUE, TRUE)) {
/* if mtmp is a shade and missile passes harmlessly through it,
give message and skip it in order to keep going */
mtmp = (struct monst *) 0;
} else if (mtmp) {
if (ohitmon(mtmp, singleobj, range, TRUE))
break;
} else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
@@ -754,7 +772,7 @@ struct attack *mattk;
break;
default:
impossible("bad attack type in spitmu");
/* fall through */
/*FALLTHRU*/
case AD_ACID:
otmp = mksobj(ACID_VENOM, TRUE, FALSE);
break;

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 uhitm.c $NHDT-Date: 1567805813 2019/09/06 21:36:53 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.212 $ */
/* NetHack 3.6 uhitm.c $NHDT-Date: 1573688694 2019/11/13 23:44:54 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.214 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -696,10 +696,8 @@ int dieroll;
long silverhit = 0L;
int wtype;
struct obj *monwep;
char unconventional[BUFSZ]; /* substituted for word "attack" in msg */
char saved_oname[BUFSZ];
unconventional[0] = '\0';
saved_oname[0] = '\0';
wakeup(mon, TRUE);
@@ -872,7 +870,6 @@ int dieroll;
} else {
if (mdat == &mons[PM_SHADE] && !shade_aware(obj)) {
tmp = 0;
Strcpy(unconventional, cxname(obj));
} else {
switch (obj->otyp) {
case BOULDER: /* 1d20 */
@@ -1143,21 +1140,14 @@ int dieroll;
poiskilled = TRUE;
}
if (tmp < 1) {
boolean mon_is_shade = (mon->data == &mons[PM_SHADE]);
/* make sure that negative damage adjustment can't result
in inadvertently boosting the victim's hit points */
tmp = 0;
if (mdat == &mons[PM_SHADE]) {
if (!hittxt) {
const char *what = *unconventional ? unconventional : "attack";
Your("%s %s harmlessly through %s.", what,
vtense(what, "pass"), mon_nam(mon));
hittxt = TRUE;
}
} else {
if (get_dmg_bonus)
tmp = 1;
}
tmp = (get_dmg_bonus && !mon_is_shade) ? 1 : 0;
if (mon_is_shade && !hittxt
&& thrown != HMON_THROWN && thrown != HMON_KICKED)
hittxt = shade_miss(&youmonst, mon, obj, FALSE, TRUE);
}
if (jousting) {
@@ -1367,6 +1357,44 @@ struct obj *obj;
return FALSE;
}
/* used for hero vs monster and monster vs monster; also handles
monster vs hero but that won't happen because hero can't be a shade */
boolean
shade_miss(magr, mdef, obj, thrown, verbose)
struct monst *magr, *mdef;
struct obj *obj;
boolean thrown, verbose;
{
const char *what, *whose, *target;
boolean youagr = (magr == &youmonst), youdef = (mdef == &youmonst);
/* we're using dmgval() for zero/not-zero, not for actual damage amount */
if (mdef->data != &mons[PM_SHADE] || (obj && dmgval(obj, mdef)))
return FALSE;
if (verbose
&& ((youdef || cansee(mdef->mx, mdef->my) || sensemon(mdef))
|| (magr == &youmonst && distu(mdef->mx, mdef->my) <= 2))) {
static const char harmless[] = " harmlessly through ";
what = (!obj || shade_aware(obj)) ? "attack" : cxname(obj);
target = youdef ? "you" : mon_nam(mdef);
if (!thrown) {
whose = youagr ? "Your" : s_suffix(Monnam(magr));
pline("%s %s %s%s%s.", whose, what,
vtense(what, "pass"), harmless, mon_nam(mdef));
} else {
pline("%s %s%s%s.", The(what), /* note: not pline_The() */
vtense(what, "pass"), harmless, mon_nam(mdef));
}
if (!youdef && !canspotmon(mdef))
map_invisible(mdef->mx, mdef->my);
}
if (!youdef)
mdef->msleeping = 0;
return TRUE;
}
/* check whether slippery clothing protects from hug or wrap attack */
/* [currently assumes that you are the attacker] */
STATIC_OVL boolean

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 zap.c $NHDT-Date: 1561927499 2019/06/30 20:44:59 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.312 $ */
/* NetHack 3.6 zap.c $NHDT-Date: 1573688696 2019/11/13 23:44:56 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.316 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
@@ -3349,13 +3349,20 @@ struct obj **pobj; /* object tossed/used, set to NULL
if (range > 3) /* another bounce? */
skiprange(range, &skiprange_start, &skiprange_end);
} else if (mtmp && M_IN_WATER(mtmp->data)) {
if ((!Blind && canseemon(mtmp)) || sensemon(mtmp))
if (!Blind && canspotmon(mtmp))
pline("%s %s over %s.", Yname2(obj), otense(obj, "pass"),
mon_nam(mtmp));
mtmp = (struct monst *) 0;
}
}
if (mtmp && !(in_skip && M_IN_WATER(mtmp->data))) {
/* if mtmp is a shade and missile passes harmlessly through it,
give message and skip it in order to keep going */
if (mtmp && (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
&& shade_miss(&youmonst, mtmp, obj, TRUE, TRUE))
mtmp = (struct monst *) 0;
if (mtmp) {
notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
if (weapon == FLASHED_LIGHT) {
/* FLASHED_LIGHT hitting invisible monster should