From f3a923d647fcfa7255f6302d718b86ba237f3c1e Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 13 Nov 2019 15:47:46 -0800 Subject: [PATCH] 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. --- doc/fixes36.3 | 3 +- include/extern.h | 4 +- src/dothrow.c | 12 ++++-- src/mhitm.c | 95 ++++++++++++++++++++++++++++++++++++++++++------ src/mhitu.c | 13 +++++-- src/mthrowu.c | 32 ++++++++++++---- src/uhitm.c | 62 ++++++++++++++++++++++--------- src/zap.c | 13 +++++-- 8 files changed, 187 insertions(+), 47 deletions(-) diff --git a/doc/fixes36.3 b/doc/fixes36.3 index 85dbbdf79..66cb47b5b 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -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 diff --git a/include/extern.h b/include/extern.h index 9467371b4..00fa63ee0 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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, diff --git a/src/dothrow.c b/src/dothrow.c index 827cdca73..c1a415a58 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -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) diff --git a/src/mhitm.c b/src/mhitm.c index 7d436ac84..c69597227 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -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) diff --git a/src/mhitu.c b/src/mhitu.c index 664955fb9..f14ddbb63 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -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; diff --git a/src/mthrowu.c b/src/mthrowu.c index 1c65e2488..7ecbbb2b2 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -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; diff --git a/src/uhitm.c b/src/uhitm.c index 29120a78a..a95595548 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -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 diff --git a/src/zap.c b/src/zap.c index 308754340..1c7a5debb 100644 --- a/src/zap.c +++ b/src/zap.c @@ -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