From: PatR Date: Sun, 28 Jan 2018 08:38:08 +0000 (-0800) Subject: fix #6691 and a couple other twoweap issues X-Git-Tag: NetHack-3.6.1_RC01~177^2~16 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=baba2acb8ddadcdb3e5c357377cbcb9d517588f5;p=nethack fix #6691 and a couple other twoweap issues Report was for dual-wielding hitting an enchanter and assumed that a resistant artifact as primary weapon was protecting vulnerable secondary weapon. Actual reason was simpler. When in normal form, dual-wielding attacks against creatures which cause erosion to the weapon which hits them would only inflict the passive erosion damage to the primary weapon, even if it missed and secondary hit. Make primary attack always trigger passive counter- attack--before second swing now, rather than after--even if it misses, and secondary attack trigger another one if that hits. Both weapons are now subject to passive erosion (but only when they actually hit); when secondary weapon hits, hero gets a double dose of counter-attack. Hero poly'd into a monster with multiple weapon attacks (various leaders: dwarf lord, orc-captain, and so forth) would try to emulate dual wielding and first hit with uwep then with uswapwep. But it would do that even if uswapwep was a bow or stack of darts that the player had no itention of using for hand-to-hand. Stick with repeat hits by uwep when uswapwep seems inappropriate. Splitting a pudding while dual-wielding would only do so when hit by uwep of appropriate material, never when hit by uswapwep. So silver saber and longsword could split if longsword was primary but never split if saber was primary. Check material and splitting separately for each hit. It's now possible to split twice with one dual-weapon attack if both weapons hit and both are made of the right material (iron or 'metal'; among relevant objects the latter is only used for tsurugi and scapel). --- diff --git a/include/extern.h b/include/extern.h index ce0b42008..afbf4e498 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1514769568 2018/01/01 01:19:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.622 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1517128658 2018/01/28 08:37:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.624 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2473,13 +2473,14 @@ E void NDECL(u_init); E void FDECL(erode_armor, (struct monst *, int)); E boolean FDECL(attack_checks, (struct monst *, struct obj *)); E void FDECL(check_caitiff, (struct monst *)); -E int FDECL(find_roll_to_hit, - (struct monst *, UCHAR_P, struct obj *, int *, int *)); +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 void FDECL(missum, (struct monst *, struct attack *, BOOLEAN_P)); -E int FDECL(passive, (struct monst *, BOOLEAN_P, int, UCHAR_P, BOOLEAN_P)); +E int FDECL(passive, (struct monst *, struct obj *, BOOLEAN_P, int, + UCHAR_P, BOOLEAN_P)); E void FDECL(passive_obj, (struct monst *, struct obj *, struct attack *)); E void FDECL(stumble_onto_mimic, (struct monst *)); E int FDECL(flash_hits_mon, (struct monst *, struct obj *)); diff --git a/src/dokick.c b/src/dokick.c index 7aa8e2baa..c1976dbc8 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 dokick.c $NHDT-Date: 1446955295 2015/11/08 04:01:35 $ $NHDT-Branch: master $:$NHDT-Revision: 1.104 $ */ +/* NetHack 3.6 dokick.c $NHDT-Date: 1517128663 2018/01/28 08:37:43 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.113 $ */ /* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -116,7 +116,7 @@ register boolean clumsy; } } - (void) passive(mon, TRUE, mon->mhp > 0, AT_KICK, FALSE); + (void) passive(mon, uarmf, TRUE, mon->mhp > 0, AT_KICK, FALSE); if (mon->mhp <= 0 && !trapkilled) killed(mon); @@ -162,7 +162,7 @@ xchar x, y; && !is_flyer(mon->data)) { pline("Floating in the air, you miss wildly!"); exercise(A_DEX, FALSE); - (void) passive(mon, FALSE, 1, AT_KICK, FALSE); + (void) passive(mon, uarmf, FALSE, 1, AT_KICK, FALSE); return; } @@ -213,13 +213,13 @@ xchar x, y; } else if (tmp > (kickdieroll = rnd(20))) { You("kick %s.", mon_nam(mon)); sum = damageum(mon, uattk); - (void) passive(mon, (boolean) (sum > 0), (sum != 2), AT_KICK, - FALSE); + (void) passive(mon, uarmf, (boolean) (sum > 0), + (sum != 2), AT_KICK, FALSE); if (sum == 2) break; /* Defender died */ } else { missum(mon, uattk, (tmp + armorpenalty > kickdieroll)); - (void) passive(mon, FALSE, 1, AT_KICK, FALSE); + (void) passive(mon, uarmf, FALSE, 1, AT_KICK, FALSE); } } return; @@ -233,7 +233,7 @@ xchar x, y; if (martial() && !rn2(2)) goto doit; Your("clumsy kick does no damage."); - (void) passive(mon, FALSE, 1, AT_KICK, FALSE); + (void) passive(mon, uarmf, FALSE, 1, AT_KICK, FALSE); return; } if (i < j / 10) @@ -257,7 +257,7 @@ doit: if (!nohands(mon->data) && !rn2(martial() ? 5 : 3)) { pline("%s blocks your %skick.", Monnam(mon), clumsy ? "clumsy " : ""); - (void) passive(mon, FALSE, 1, AT_KICK, FALSE); + (void) passive(mon, uarmf, FALSE, 1, AT_KICK, FALSE); return; } else { maybe_mnexto(mon); @@ -274,7 +274,7 @@ doit: ? "slides" : "jumps", clumsy ? "easily" : "nimbly", clumsy ? "clumsy " : ""); - (void) passive(mon, FALSE, 1, AT_KICK, FALSE); + (void) passive(mon, uarmf, FALSE, 1, AT_KICK, FALSE); return; } } diff --git a/src/uhitm.c b/src/uhitm.c index df0645b96..daa069cc4 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 uhitm.c $NHDT-Date: 1513297347 2017/12/15 00:22:27 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.172 $ */ +/* NetHack 3.6 uhitm.c $NHDT-Date: 1517128664 2018/01/28 08:37:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.173 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -392,11 +392,10 @@ register struct monst *mtmp; unweapon = FALSE; if (flags.verbose) { if (uwep) - You("begin bashing monsters with %s.", - yobjnam(uwep, (char *) 0)); + You("begin bashing monsters with %s.", yname(uwep)); else if (!cantwield(youmonst.data)) - You("begin %sing monsters with your %s %s.", - Role_if(PM_MONK) ? "strik" : "bash", + You("begin %s monsters with your %s %s.", + ing_suffix(Role_if(PM_MONK) ? "strike" : "bash"), uarmg ? "gloved" : "bare", /* Del Lamb */ makeplural(body_part(HAND))); } @@ -541,7 +540,7 @@ struct attack *uattk; mhit = (tmp > dieroll); result = known_hitum(mtmp, uwep, &mhit, tmp, armorpenalty, uattk, dieroll); - (void) passive(mtmp, mhit, DEADMONSTER(mtmp), AT_WEAP, !uwep); + (void) passive(mtmp, uwep, mhit, DEADMONSTER(mtmp), AT_WEAP, !uwep); if (mon == mtmp) malive = result; } @@ -570,6 +569,10 @@ struct attack *uattk; if (tmp > dieroll) exercise(A_DEX, TRUE); malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk, dieroll); + if (wepbefore && !uwep) + wep_was_destroyed = TRUE; + (void) passive(mon, uwep, mhit, malive, AT_WEAP, wep_was_destroyed); + /* second attack for two-weapon combat; won't occur if Stormbringer overrode confirmation (assumes Stormbringer is primary weapon) or if the monster was killed or knocked to different location */ @@ -580,10 +583,10 @@ struct attack *uattk; mhit = (tmp > dieroll || u.uswallow); malive = known_hitum(mon, uswapwep, &mhit, tmp, armorpenalty, uattk, dieroll); + /* second passive counter-attack only occurs if second attack hits */ + if (mhit) + (void) passive(mon, uswapwep, mhit, malive, AT_WEAP, !uswapwep); } - if (wepbefore && !uwep) - wep_was_destroyed = TRUE; - (void) passive(mon, mhit, malive, AT_WEAP, wep_was_destroyed); return malive; } @@ -1157,8 +1160,9 @@ int dieroll; if ((mdat == &mons[PM_BLACK_PUDDING] || mdat == &mons[PM_BROWN_PUDDING]) /* pudding is alive and healthy enough to split */ && mon->mhp > 1 && !mon->mcan - /* iron weapon using melee or polearm hit [3.6.1: metal weapon too] */ - && obj && obj == uwep + /* iron weapon using melee or polearm hit [3.6.1: metal weapon too; + also allow either or both weapons to cause split when twoweap] */ + && obj && (obj == uwep || (u.twoweap && obj == uswapwep)) && ((objects[obj->otyp].oc_material == IRON /* allow scalpel and tsurugi to split puddings */ || objects[obj->otyp].oc_material == METAL) @@ -1166,7 +1170,12 @@ int dieroll; && !(is_ammo(obj) || is_missile(obj))) && hand_to_hand) { if (clone_mon(mon, 0, 0)) { - pline("%s divides as you hit it!", Monnam(mon)); + char withwhat[BUFSZ]; + + withwhat[0] = '\0'; + if (u.twoweap && flags.verbose) + Sprintf(withwhat, " with %s", yname(obj)); + pline("%s divides as you hit it%s!", Monnam(mon), withwhat); hittxt = TRUE; } } @@ -2186,7 +2195,7 @@ hmonas(mon) register struct monst *mon; { struct attack *mattk, alt_attk; - struct obj *weapon; + struct obj *weapon, **originalweapon; boolean altwep = FALSE, weapon_used = FALSE; int i, tmp, armorpenalty, sum[NATTK], nsum = 0, dhit = 0, attknum = 0; int dieroll; @@ -2194,6 +2203,7 @@ register struct monst *mon; for (i = 0; i < NATTK; i++) { sum[i] = 0; mattk = getmattk(&youmonst, mon, i, sum, &alt_attk); + weapon = 0; switch (mattk->aatyp) { case AT_WEAP: use_weapon: @@ -2208,20 +2218,36 @@ register struct monst *mon; * we currently allow the player to get each of these as a weapon * attack. Is this really desirable? */ - /* approximate two-weapon mode */ - weapon = (altwep && uswapwep) ? uswapwep : uwep; - altwep = !altwep; /* toggle for next attack */ + /* approximate two-weapon mode; known_hitum() -> hmon() -> &c + might destroy the weapon argument, but it might also already + be Null, and we want to track that for passive() */ + originalweapon = (altwep && uswapwep) ? &uswapwep : &uwep; + if (uswapwep /* set up 'altwep' flag for next iteration */ + /* only switch to uswapwep if it's a weapon */ + && (uswapwep->oclass == WEAPON_CLASS || is_weptool(uswapwep)) + /* only switch if uswapwep is not bow, arrows, or darts */ + && !(is_launcher(uswapwep) || is_ammo(uswapwep) + || is_missile(uswapwep))) /* dart, shuriken, boomerang */ + altwep = !altwep; /* toggle for next attack */ + weapon = *originalweapon; + if (!weapon) /* no need to go beyond no-gloves to rings; not ...*/ + originalweapon = &uarmg; /*... subject to erosion damage */ + tmp = find_roll_to_hit(mon, AT_WEAP, weapon, &attknum, &armorpenalty); dieroll = rnd(20); dhit = (tmp > dieroll || u.uswallow); /* Enemy dead, before any special abilities used */ - if (!known_hitum(mon, weapon, &dhit, tmp, armorpenalty, mattk, - dieroll)) { + if (!known_hitum(mon, weapon, &dhit, tmp, + armorpenalty, mattk, dieroll)) { sum[i] = 2; break; } else sum[i] = dhit; + /* originalweapon points to an equipment slot which might + now be empty if the weapon was destroyed during the hit; + passive(,weapon,...) won't call passive_obj() in that case */ + weapon = *originalweapon; /* might receive passive erosion */ /* might be a worm that gets cut in half */ if (m_at(u.ux + u.dx, u.uy + u.dy) != mon) return (boolean) (nsum != 0); @@ -2366,11 +2392,11 @@ register struct monst *mon; u.mh = -1; /* dead in the current form */ rehumanize(); } - if (sum[i] == 2) - return (boolean) passive(mon, 1, 0, mattk->aatyp, FALSE); - /* defender dead */ - else { - (void) passive(mon, sum[i], 1, mattk->aatyp, FALSE); + if (sum[i] == 2) { + /* defender dead */ + return (boolean) passive(mon, weapon, 1, 0, mattk->aatyp, FALSE); + } else { + (void) passive(mon, weapon, sum[i], 1, mattk->aatyp, FALSE); nsum |= sum[i]; } if (!Upolyd) @@ -2384,10 +2410,11 @@ register struct monst *mon; /* Special (passive) attacks on you by monsters done here. */ int -passive(mon, mhit, malive, aatyp, wep_was_destroyed) -register struct monst *mon; -register boolean mhit; -register int malive; +passive(mon, weapon, mhit, malive, aatyp, wep_was_destroyed) +struct monst *mon; +struct obj *weapon; /* uwep or uswapwep or uarmg or uarmf or Null */ +boolean mhit; +int malive; uchar aatyp; boolean wep_was_destroyed; { @@ -2412,14 +2439,14 @@ boolean wep_was_destroyed; */ switch (ptr->mattk[i].adtyp) { case AD_FIRE: - if (mhit && !mon->mcan) { + if (mhit && !mon->mcan && weapon) { if (aatyp == AT_KICK) { if (uarmf && !rn2(6)) (void) erode_obj(uarmf, xname(uarmf), ERODE_BURN, EF_GREASE | EF_VERBOSE); } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH) - passive_obj(mon, (struct obj *) 0, &(ptr->mattk[i])); + passive_obj(mon, weapon, &(ptr->mattk[i])); } break; case AD_ACID: @@ -2435,14 +2462,14 @@ boolean wep_was_destroyed; if (!rn2(30)) erode_armor(&youmonst, ERODE_CORRODE); } - if (mhit) { + if (mhit && weapon) { if (aatyp == AT_KICK) { if (uarmf && !rn2(6)) (void) erode_obj(uarmf, xname(uarmf), ERODE_CORRODE, EF_GREASE | EF_VERBOSE); } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH) - passive_obj(mon, (struct obj *) 0, &(ptr->mattk[i])); + passive_obj(mon, weapon, &(ptr->mattk[i])); } exercise(A_STR, FALSE); break; @@ -2471,25 +2498,25 @@ boolean wep_was_destroyed; } break; case AD_RUST: - if (mhit && !mon->mcan) { + if (mhit && !mon->mcan && weapon) { if (aatyp == AT_KICK) { if (uarmf) (void) erode_obj(uarmf, xname(uarmf), ERODE_RUST, EF_GREASE | EF_VERBOSE); } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH) - passive_obj(mon, (struct obj *) 0, &(ptr->mattk[i])); + passive_obj(mon, weapon, &(ptr->mattk[i])); } break; case AD_CORR: - if (mhit && !mon->mcan) { + if (mhit && !mon->mcan && weapon) { if (aatyp == AT_KICK) { if (uarmf) (void) erode_obj(uarmf, xname(uarmf), ERODE_CORRODE, EF_GREASE | EF_VERBOSE); } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH) - passive_obj(mon, (struct obj *) 0, &(ptr->mattk[i])); + passive_obj(mon, weapon, &(ptr->mattk[i])); } break; case AD_MAGM: @@ -2504,17 +2531,14 @@ boolean wep_was_destroyed; break; case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ if (mhit) { - struct obj *obj = (struct obj *) 0; - if (aatyp == AT_KICK) { - obj = uarmf; - if (!obj) + if (!weapon) break; } else if (aatyp == AT_BITE || aatyp == AT_BUTT || (aatyp >= AT_STNG && aatyp < AT_WEAP)) { break; /* no object involved */ } - passive_obj(mon, obj, &(ptr->mattk[i])); + passive_obj(mon, weapon, &(ptr->mattk[i])); } break; default: @@ -2628,6 +2652,7 @@ struct attack *mattk; /* null means we find one internally */ struct permonst *ptr = mon->data; int i; + /* [this first bit is obsolete; we're not called with Null anymore] */ /* if caller hasn't specified an object, use uwep, uswapwep or uarmg */ if (!obj) { obj = (u.twoweap && uswapwep && !rn2(2)) ? uswapwep : uwep;