From 36e4d80547dc2e56416e09788b0b8d0542c429dd Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 2 Mar 2018 18:19:23 -0800 Subject: [PATCH] Cleaver update I worked on this a while back but didn't commit it because I couldn't figure out the dead monster which wouldn't die. Pasi beat me to that. Clean up the Cleaver code some and make the three-target swing alternate between counter-clockwise and clockwise to simulate normal right-to-left swing followed by left-to-right backswing. (Alternation happens on each swing regardless of whether it is consecutive with the most recent one. That's suboptimal but easy....) Also, stop three-target attack early if hero is life-saved after being killed by a passive counter-attack from the first or second target. Prevent rogue backstab and two-hander breaking foes' weapons when using Cleaver hand-to-hand because getting more than one of either of those bonuses at a time would be excessive. I think allowing those for the primary target but not for the two adjacent ones would be better, but I just thought of that and am not going back for another round of revising. This doesn't incorporate the two pull requests: one to avoid hitting tame or peaceful adjacent targets unless the primary was also tame or peaceful, the other to avoid hitting unseen adjacent targets. I'm not sure if that includes remembered-unseen 'I' on the spot or no monster shown at all. (There's a third one about updating the map but it isn't needed for the existing Cleaver code either before or after this patch.) I'm in favor of the first one and am not sure about the second. My original concern was that someone could use Cleaver to find/hit three unknown monsters at a time via 'F' prefix, but forcefight aimed at thin air doesn't reach the Cleaver code so that can't happen, nor can attacking two known close monsters at once by targetting an empty spot between them. --- src/uhitm.c | 109 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 41 deletions(-) diff --git a/src/uhitm.c b/src/uhitm.c index 6807df056..bfc103e0a 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 uhitm.c $NHDT-Date: 1517128664 2018/01/28 08:37:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.173 $ */ +/* NetHack 3.6 uhitm.c $NHDT-Date: 1520043553 2018/03/03 02:19:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.175 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -14,8 +14,7 @@ STATIC_DCL boolean FDECL(hmon_hitmon, (struct monst *, struct obj *, int, int)); STATIC_DCL int FDECL(joust, (struct monst *, struct obj *)); STATIC_DCL void NDECL(demonpet); -STATIC_DCL boolean FDECL(m_slips_free, (struct monst * mtmp, - struct attack *mattk)); +STATIC_DCL boolean FDECL(m_slips_free, (struct monst *, struct attack *)); STATIC_DCL int FDECL(explum, (struct monst *, struct attack *)); STATIC_DCL void FDECL(start_engulf, (struct monst *)); STATIC_DCL void NDECL(end_engulf); @@ -489,63 +488,85 @@ int dieroll; return malive; } -/* hit the monster next to you and the monsters to the left and right of it */ +/* hit the monster next to you and the monsters to the left and right of it; + return False if the primary target is killed, True otherwise */ STATIC_OVL boolean -hitum_cleave(mon, uattk) -struct monst *mon; -struct attack *uattk; +hitum_cleave(target, uattk) +struct monst *target; /* non-Null; forcefight at nothing doesn't cleave... */ +struct attack *uattk; /* ... but we don't enforce that here; Null works ok */ { - int i = 0; - int x = u.ux; - int y = u.uy; - int count = 3; - boolean malive = TRUE; - struct monst *mtmp; - - /* find the direction we're swinging */ - while (i < 8) { + /* swings will be delivered in alternate directions; with consecutive + attacks it will simulate normal swing and backswing; when swings + are non-consecutive, hero will sometimes start a series of attacks + with a backswing--that doesn't impact actual play, just spoils the + simulation attempt a bit */ + static boolean clockwise = FALSE; + unsigned i; + int count, umort, x = u.ux, y = u.uy; + + /* find the direction toward primary target */ + for (i = 0; i < 8; ++i) if (xdir[i] == u.dx && ydir[i] == u.dy) break; - i++; - } - if (i == 8) { - impossible("hitum_cleave: failed to find target monster?"); - return TRUE; + impossible("hitum_cleave: unknown target direction [%d,%d,%d]?", + u.dx, u.dy, u.dz); + return TRUE; /* target hasn't been killed */ } - i = (i + 2) % 8; + clockwise = !clockwise; /* alternate */ + /* adjust direction by two so that loop's increment (for clockwise) + or decrement (for counter-clockwise) will point at the spot next + to primary target */ + if (clockwise) + i = (i + 6) % 8; + else + i = (i + 2) % 8; + umort = u.umortality; /* used to detect life-saving */ - /* swing from right to left */ - while (count-- && uwep) { - boolean result; - int tmp, dieroll, mhit, attknum, armorpenalty; + /* + * Three attacks: adjacent to primary, primary, adjacent on other + * side. Primary target must be present or we wouldn't have gotten + * here (forcefight at thin air won't 'cleave'). However, the + * first attack might kill it (gas spore explosion, weak long worm + * occupying both spots) so we don't assume that it's still present + * on the second attack. + */ + for (count = 3; count > 0; --count) { + struct monst *mtmp; + int tx, ty, tmp, dieroll, mhit, attknum, armorpenalty; - if (!i) - i = 7; + if (clockwise) + i = (i + 1) % 8; /* ++i, wrap 8 to i=0 */ else - i--; + i = (i + 7) % 8; /* --i, wrap -1 to i=7 */ - mtmp = NULL; - if (isok(x + xdir[i], y + ydir[i])) - mtmp = m_at(x + xdir[i], y + ydir[i]); + tx = x + xdir[i], ty = y + ydir[i]; /* current target location */ + if (!isok(tx, ty)) + continue; + mtmp = m_at(tx, ty); if (!mtmp) { - (void) unmap_invisible(x + xdir[i], y + ydir[i]); + if (glyph_is_invisible(levl[tx][ty].glyph)) + (void) unmap_invisible(tx, ty); continue; } - tmp = find_roll_to_hit(mtmp, uattk->aatyp, uwep, &attknum, &armorpenalty); dieroll = rnd(20); mhit = (tmp > dieroll); - result = known_hitum(mtmp, uwep, &mhit, tmp, armorpenalty, - uattk, dieroll); + (void) known_hitum(mtmp, uwep, &mhit, tmp, armorpenalty, + uattk, dieroll); (void) passive(mtmp, uwep, mhit, !DEADMONSTER(mtmp), AT_WEAP, !uwep); - if (mon == mtmp) - malive = result; + + /* stop attacking if weapon is gone or hero got killed and + life-saved after passive counter-attack */ + if (!uwep || u.umortality > umort) + break; } - return malive; + /* return False if primary target died, True otherwise; note: if 'target' + was nonNull upon entry then it's still nonNull even if *target died */ + return (target && DEADMONSTER(target)) ? FALSE : TRUE; } /* hit target monster; returns TRUE if it still lives */ @@ -562,7 +583,10 @@ struct attack *uattk; int dieroll = rnd(20); int mhit = (tmp > dieroll || u.uswallow); - if (uwep && uwep->oartifact == ART_CLEAVER + /* Cleaver attacks three spots, one on either side of 'mon'; + it can't we part of dual-wielding but we guard against that anyway; + cleave return value reflects status of primary target ('mon') */ + if (uwep && uwep->oartifact == ART_CLEAVER && !u.twoweap && !u.uswallow && !u.ustuck && !NODIAG(u.umonnum)) return hitum_cleave(mon, uattk); @@ -718,7 +742,10 @@ int dieroll; tmp = dmgval(obj, mon); /* a minimal hit doesn't exercise proficiency */ valid_weapon_attack = (tmp > 1); - if (!valid_weapon_attack || mon == u.ustuck || u.twoweap) { + if (!valid_weapon_attack || mon == u.ustuck || u.twoweap + /* Cleaver can hit up to three targets at once so don't + let it also hit from behind or shatter foes' weapons */ + || (hand_to_hand && obj->oartifact == ART_CLEAVER)) { ; /* no special bonuses */ } else if (mon->mflee && Role_if(PM_ROGUE) && !Upolyd /* multi-shot throwing is too powerful here */ -- 2.50.0