]> granicus.if.org Git - nethack/commitdiff
muse looting
authorPatR <rankin@nethack.org>
Sun, 2 Feb 2020 23:22:42 +0000 (15:22 -0800)
committerPatR <rankin@nethack.org>
Sun, 2 Feb 2020 23:22:42 +0000 (15:22 -0800)
Give better feedback than "<Monster> rummages through something"
when hero sees a monster looting a container.

Have monster take out up to 4 items at a time instead of always 1.
(Hero can take out an arbitrary number in one move.)

When deciding what to try to take out, 'count' (now 'nitems') was
initialized to 1 instead of 0, giving the monster a 1 out of N+1
chance of not trying to take anything out.  It wasn't clear whether
that was intentional (there's already a chance for not taking things
out when deciding whether to use the container).  I kept that result
in and made it more explicit now.

When deciding whether the chosen item could be moved from container
to monster's inventory, the can_carry(item) check was counting the
weight of the item twice, once explicitly when considering adding it
to minvent but also implicitly as part of the carried container's
weight already in minvent.

src/muse.c

index f31a7667fd0fbaca66ba1f1d755ccd8bc03f02a3..bcb191f9f8aa8f572f51e55875415b9a48c17f99 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 muse.c  $NHDT-Date: 1574648940 2019/11/25 02:29:00 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.115 $ */
+/* NetHack 3.6 muse.c  $NHDT-Date: 1580685754 2020/02/02 23:22:34 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.119 $ */
 /*      Copyright (C) 1990 by Ken Arromdee                         */
 /* NetHack may be freely redistributed.  See license for details.  */
 
@@ -14,7 +14,6 @@
  * are confused don't know not to read scrolls, etc....
  */
 
-static struct permonst *FDECL(muse_newcham_mon, (struct monst *));
 static int FDECL(precheck, (struct monst *, struct obj *));
 static void FDECL(mzapwand, (struct monst *, struct obj *, BOOLEAN_P));
 static void FDECL(mplayhorn, (struct monst *, struct obj *, BOOLEAN_P));
@@ -25,6 +24,9 @@ static int FDECL(mbhitm, (struct monst *, struct obj *));
 static void FDECL(mbhit, (struct monst *, int,
                               int FDECL((*), (MONST_P, OBJ_P)),
                               int FDECL((*), (OBJ_P, OBJ_P)), struct obj *));
+static struct permonst *FDECL(muse_newcham_mon, (struct monst *));
+static int FDECL(mloot_container, (struct monst *mon, struct obj *,
+                                   BOOLEAN_P));
 static void FDECL(you_aggravate, (struct monst *));
 static void FDECL(mon_consume_unstone, (struct monst *, struct obj *,
                                             BOOLEAN_P, BOOLEAN_P));
@@ -1668,12 +1670,26 @@ struct monst *mtmp;
     if (nohands(mdat))
         return 0;
 
-#define nomore(x)       if (g.m.has_misc == x) continue
+    /* normally we would want to bracket a macro expansion containing
+       'if' without matching 'else' with 'do { ... } while (0)' but we
+       can't do that here because it would intercept 'continue' */
+#define nomore(x)       if (g.m.has_misc == (x)) continue
     /*
      * [bug?]  Choice of item is not prioritized; the last viable one
      * in the monster's inventory will be chosen.
      * 'nomore()' is nearly worthless because it only screens checking
      * of duplicates when there is no alternate type in between them.
+     *
+     * MUSE_BAG issues:
+     * should allow looting floor container instead of needing the
+     * monster to have picked it up and now be carrying it which takes
+     * extra time and renders heavily filled containers immune;
+     * hero should have a chance to see the monster fail to open a
+     * locked container instead of monster always knowing lock state
+     * (may not be feasible to implement--requires too much per-object
+     * info for each monster);
+     * monster with key should be able to unlock a locked floor
+     * container and not know whether it is trapped.
      */
     for (obj = mtmp->minvent; obj; obj = obj->nobj) {
         /* Monsters shouldn't recognize cursed items; this kludge is
@@ -1768,6 +1784,116 @@ struct monst *mon;
     return rndmonst();
 }
 
+static int
+mloot_container(mon, container, vismon)
+struct monst *mon;
+struct obj *container;
+boolean vismon;
+{
+    char contnr_nam[BUFSZ], mpronounbuf[20];
+    boolean nearby;
+    int takeout_indx, takeout_count, howfar, res = 0;
+
+    if (!container || !Has_contents(container) || container->olocked)
+        return res; /* 0 */
+    /* FIXME: handle cursed bag of holding */
+    if (Is_mbag(container) && container->cursed)
+        return res; /* 0 */
+
+    switch (rn2(10)) {
+    default: /* case 0, 1, 2, 3: */
+        takeout_count = 1;
+        break;
+    case 4: case 5: case 6:
+        takeout_count = 2;
+        break;
+    case 7: case 8:
+        takeout_count = 3;
+        break;
+    case 9:
+        takeout_count = 4;
+        break;
+    }
+    howfar = distu(mon->mx, mon->my);
+    nearby = (howfar <= 7 * 7);
+    contnr_nam[0] = mpronounbuf[0] = '\0';
+    if (vismon) {
+        /* do this once so that when hallucinating it won't change
+           from one item to the next */
+        Strcpy(mpronounbuf, mhe(mon));
+    }
+
+    for (takeout_indx = 0; takeout_indx < takeout_count; ++takeout_indx) {
+        struct obj *xobj;
+        int nitems;
+
+        if (!Has_contents(container)) /* might have removed all items */
+            break;
+        /* TODO?
+         *  Monster ought to prioritize on something it wants to use.
+         */
+        nitems = 0;
+        for (xobj = container->cobj; xobj != 0; xobj = xobj->nobj)
+            ++nitems;
+        /* nitems is always greater than 0 due to Has_contents() check;
+           throttle item removal as the container becomes less filled */
+        if (!rn2(nitems + 1))
+            break;
+        nitems = rn2(nitems);
+        for (xobj = container->cobj; nitems > 0; xobj = xobj->nobj)
+            --nitems;
+
+        container->cknown = 0; /* hero no longer knows container's contents
+                                * even if [attempted] removal is observed */
+        if (!*contnr_nam) {
+            /* xname sets dknown, distant_name doesn't */
+            Strcpy(contnr_nam, nearby ? xname(container)
+                                      : distant_name(container, xname));
+        }
+        /* this was originally just 'can_carry(mon, xobj)' which
+           covers objects a monster shouldn't pick up but also
+           checks carrying capacity; for that, it ended up counting
+           xobj's weight twice when container is carried; so take
+           xobj out, check whether it can be carried, and then put
+           it back (below) if it can't be */
+        obj_extract_self(xobj); /* this reduces container's weight */
+        /* check whether mon can handle xobj and whether weight of xobj plus
+           minvent (including container, now without xobj) can be carried */
+        if (can_carry(mon, xobj)) {
+            if (vismon) {
+                if (howfar > 2) /* not adjacent */
+                    Norep("%s rummages through %s.", Monnam(mon), contnr_nam);
+                else if (takeout_indx == 0) /* adjacent, first item */
+                    pline("%s removes %s from %s.", Monnam(mon),
+                          an(xname(xobj)), contnr_nam);
+                else /* adjacent, additional items */
+                    pline("%s removes %s.", upstart(mpronounbuf),
+                          an(xname(xobj)));
+            }
+            /* obj_extract_self(xobj); -- already done above */
+            (void) mpickobj(mon, xobj);
+            res = 2;
+        } else { /* couldn't carry xobj separately so put back inside */
+            /* an achievement prize (castle's wand?) might already be
+               marked nomerge (when it hasn't been in invent yet) */
+            boolean already_nomerge = xobj->nomerge != 0,
+                    just_xobj = !Has_contents(container);
+
+            /* this doesn't restore the original contents ordering
+               [shouldn't be a problem; even though this item didn't
+               give the rummage message, that's what mon was doing] */
+            xobj->nomerge = 1;
+            xobj = add_to_container(container, xobj);
+            if (!already_nomerge)
+                xobj->nomerge = 0;
+            container->owt = weight(container);
+            if (just_xobj)
+                break; /* out of takeout_count loop */
+        } /* can_carry */
+    } /* takeout_count */
+    return res;
+}
+
 int
 use_misc(mtmp)
 struct monst *mtmp;
@@ -1884,28 +2010,7 @@ struct monst *mtmp;
         m_useup(mtmp, otmp);
         return 2;
     case MUSE_BAG:
-        {
-            struct obj *xobj;
-            long count = 1;
-
-            /* FIXME: handle cursed bag of holding */
-            if (Is_mbag(otmp) && otmp->cursed)
-                return 0;
-            if (!Has_contents(otmp) || otmp->olocked)
-                return 0;
-
-            for (xobj = otmp->cobj; xobj; xobj = xobj->nobj) count++;
-            count = rn2(count);
-            for (xobj = otmp->cobj; xobj && count; xobj = xobj->nobj) count--;
-            if (xobj && can_carry(mtmp, xobj)) {
-                if (vismon)
-                    pline("%s rummages through something.", Monnam(mtmp));
-                obj_extract_self(xobj);
-                (void) mpickobj(mtmp, xobj);
-                return 2;
-            }
-        }
-        return 0;
+        return mloot_container(mtmp, otmp, vismon);
     case MUSE_POLY_TRAP:
         if (vismon) {
             const char *Mnam = Monnam(mtmp);