]> granicus.if.org Git - nethack/commitdiff
Cleaver update
authorPatR <rankin@nethack.org>
Sat, 3 Mar 2018 02:19:23 +0000 (18:19 -0800)
committerPatR <rankin@nethack.org>
Sat, 3 Mar 2018 02:19:23 +0000 (18:19 -0800)
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

index 6807df05617a0323535e28de14a37fc58d4214e8..bfc103e0a3da9abb5d29d022b56676f90d8a943e 100644 (file)
@@ -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 */