From 3506c24d39c70173063cf7527853a3a039204e90 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 18 Jan 2019 13:22:43 -0800 Subject: [PATCH 1/2] fix #H7980 - multiple weapon attacks when poly'd This fixes the weapon related aspects of #H7980: having an alternate weapon be used in cases where it shouldn't when polymorphed into a monster form with multiple weapon attacks. The most egregious was using an off-hand artifact, but it would also use off-hand two-handed weapon, off-hand silver weapon when in silver-hating form, or any reasonable off-hand weapon when wearing a shield. That last is iffy whether or not to allow, since you'll still get the extra attacks whether it switches to secondary weapon or stays with the primary. I've made it re-use the primary since two-weapon mode doesn't allow a shield. The other oddity was being able to use the secondary weapon on the second swing even if the first swing was weaponless. I went with ingoring the secondary weapon if there's no primary one or if the primary is two-handed. Report included "cursed secondary doesn't weld" but that has nothing to do with polymorph attacking. I've changed that to drop the weapon if you attack with it when it's cursed, similar to what happens when secondary weapon becomes cursed while two-weaponing. It also included "marilith's attacks beyond the second don't use any weapon and can hit cockatrices without touching them". A marilith has two weapon attacks and then four claw attacks. Claw attacks only use the weapon if it hasn't been used yet, so marilith hits with primary, secondary (or primary a second time if no secondary), claw, claw, claw, claw and that's the intended behavior. It is able to hit cockatrices if wielding anything at all, same as a monster with just a single attack. Since it is impossible to wield six weapons or three pairs of gloves, that has to be intended behavior too. Playability trumps realism even if it is silly to hit without a 3rd through 6th weapon and be safe from touching the target due to the 1st weapon or one pair of gloves. [Situation is different from having no control over unsafely biting something after making a safe weapon or claw attack; perhaps a better solution would be to refrain from using the four claw attacks when attacking something that is fatal to touch.] --- doc/fixes36.2 | 7 +++++- src/uhitm.c | 60 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index d796f3260..4cc91f5a5 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.230 $ $NHDT-Date: 1547680081 2019/01/16 23:08:01 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.231 $ $NHDT-Date: 1547846557 2019/01/18 21:22:37 $ 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, @@ -337,6 +337,11 @@ entering Ft.Ludios with a lit candle lit up the entire entry room except for to prevent those wall spots from showing so soon boulder dropped or launched by a monster onto a monster trapped in a pit and killing it credited/blamed the hero and might trigger a deltrap panic +the simulation of dual weapon combat when polymorphed into a form with more + than one weapon attack allowed off-hand artifact to be used; it also + used seconary weapon even when wearing a shield, or if it was silver + even when current shape couldn't handle silver, or if it was cursed; + cursed is allowed but weapon will be dropped, just like in two-weapon Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/uhitm.c b/src/uhitm.c index 986f9c3bd..7b4ca1a84 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 uhitm.c $NHDT-Date: 1547680084 2019/01/16 23:08:04 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.199 $ */ +/* NetHack 3.6 uhitm.c $NHDT-Date: 1547846557 2019/01/18 21:22:37 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.200 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2275,7 +2275,7 @@ boolean wouldhavehit; wakeup(mdef, TRUE); } -/* attack monster as a monster. */ +/* attack monster as a monster; returns True if mon survives */ STATIC_OVL boolean hmonas(mon) register struct monst *mon; @@ -2293,6 +2293,12 @@ register struct monst *mon; switch (mattk->aatyp) { case AT_WEAP: use_weapon: + /* 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 + never have the opportunity to use two weapons) */ + if (weapon_used && sum[i - 1] && uwep && bimanual(uwep)) + continue; /* Certain monsters don't use weapons when encountered as enemies, * but players who polymorph into them have hands or claws and * thus should be able to use weapons. This shouldn't prohibit @@ -2309,11 +2315,23 @@ register struct monst *mon; 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 consider seconary when wielding one-handed primary */ + && uwep && !bimanual(uwep) + /* only switch if not wearing shield and not at artifact; + shield limitation is iffy since still get extra swings + if polyform has them, but it matches twoweap behavior; + twoweap also only allows primary to be an artifact, so + if alternate weapon is one, don't use it */ + && !uarms && !uswapwep->oartifact /* 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 */ + || is_missile(uswapwep)) /* dart, shuriken, boomerang */ + /* and not two-handed and not incapable of being wielded */ + && !bimanual(uswapwep) + && !(objects[uswapwep->otyp].oc_material == SILVER + && Hate_silver)) altwep = !altwep; /* toggle for next attack */ weapon = *originalweapon; if (!weapon) /* no need to go beyond no-gloves to rings; not ...*/ @@ -2323,9 +2341,9 @@ register struct monst *mon; &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)) { + /* enemy dead, before any special abilities used */ sum[i] = 2; break; } else @@ -2334,12 +2352,13 @@ register struct monst *mon; 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); - /* Do not print "You hit" message, since known_hitum - * already did it. - */ + /* might be a worm that gets cut in half; if so, early return */ + if (m_at(u.ux + u.dx, u.uy + u.dy) != mon) { + i = NATTK; /* skip additional attacks */ + /* proceed with uswapwep->cursed check, then exit loop */ + goto passivedone; + } + /* 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); break; @@ -2481,17 +2500,34 @@ register struct monst *mon; } if (sum[i] == 2) { /* defender dead */ - return (boolean) passive(mon, weapon, 1, 0, mattk->aatyp, FALSE); + (void) passive(mon, weapon, 1, 0, mattk->aatyp, FALSE); + nsum = 0; /* return value below used to be 'nsum > 0' */ } else { (void) passive(mon, weapon, sum[i], 1, mattk->aatyp, FALSE); nsum |= sum[i]; } + + /* don't use sum[i] beyond this point; + 'i' will be out of bounds if we get here via 'goto' */ + passivedone: + /* when using dual weapons, cursed secondary weapon doesn't weld, + it gets dropped; do the same when multiple AT_WEAP attacks + simulate twoweap */ + if (uswapwep && weapon == uswapwep && weapon->cursed) { + drop_uswapwep(); + break; /* don't proceed with additional attacks */ + } + /* stop attacking if defender has died; + needed to defer this until after uswapwep->cursed check */ + if (DEADMONSTER(mon)) + break; if (!Upolyd) break; /* No extra attacks if no longer a monster */ if (multi < 0) break; /* If paralyzed while attacking, i.e. floating eye */ } - return (boolean) (nsum != 0); + /* return value isn't used, but make it match hitum()'s */ + return !DEADMONSTER(mon); } /* Special (passive) attacks on you by monsters done here. From 9a39618fb397524c05c646ed1a9a959fb5bf34fa Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 18 Jan 2019 14:13:30 -0800 Subject: [PATCH 2/2] fix #H7983 - inconsistent shop 'for sale' behavior Items on floor in the free spot one step inside a shop's doorway were showing shop sell prices. Treat items on that spot as if they were flagged no_charge as on the floor of other shop squares. Report stated that sometimes they showed a 'for sale' price and sometimes they didn't, but I didn't see any cases where they didn't. --- doc/fixes36.2 | 4 +++- src/shk.c | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 4cc91f5a5..141c13584 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.231 $ $NHDT-Date: 1547846557 2019/01/18 21:22:37 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.232 $ $NHDT-Date: 1547849604 2019/01/18 22:13:24 $ 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, @@ -395,6 +395,8 @@ when in a shop and using ':' to look inside a container on shop floor, items the hero owned the container when engulfed while in a shop, dropping an item into the engulfer and then using ':' to look at current location could cause a crash +when items were on the floor just inside a shop's door where the shopkeeper + doesn't buy and sell stuff, those items showed a 'for sale' price tty: turn off an optimization that is the suspected cause of Windows reported partial status lines following level changes tty: ensure that current status fields are always copied to prior status diff --git a/src/shk.c b/src/shk.c index b3c0a8dd4..154924550 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 shk.c $NHDT-Date: 1546770990 2019/01/06 10:36:30 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.152 $ */ +/* NetHack 3.6 shk.c $NHDT-Date: 1547849604 2019/01/18 22:13:24 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.153 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1983,6 +1983,7 @@ int *nochrg; /* alternate return value: 1: no charge, 0: shop owned, */ struct monst *shkp; struct obj *top; xchar x, y; + boolean freespot; long cost = 0L; *nochrg = -1; /* assume 'not applicable' */ @@ -1993,11 +1994,15 @@ int *nochrg; /* alternate return value: 1: no charge, 0: shop owned, */ && (shkp = shop_keeper(inside_shop(x, y))) != 0 && inhishop(shkp)) { for (top = obj; top->where == OBJ_CONTAINED; top = top->ocontainer) continue; - *nochrg = (top->where == OBJ_FLOOR && obj->no_charge); + freespot = (top->where == OBJ_FLOOR + && x == ESHK(shkp)->shk.x && y == ESHK(shkp)->shk.y); + /* no_charge is only set for floor items inside shop proper; + items on freespot are implicitly 'no charge' */ + *nochrg = (top->where == OBJ_FLOOR && (obj->no_charge || freespot)); if (carried(top) ? (int) obj->unpaid : !*nochrg) cost = obj->quan * get_cost(obj, shkp); - if (Has_contents(obj)) + if (Has_contents(obj) && !freespot) cost += contained_cost(obj, shkp, 0L, FALSE, FALSE); } return cost;