From: PatR Date: Sun, 2 Feb 2020 23:22:42 +0000 (-0800) Subject: muse looting X-Git-Tag: NetHack-3.7.0_WIP-2020-02-14~71 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=00ce339c1550bd99d93275b6ff0926ca2b57a11b;p=nethack muse looting Give better feedback than " 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. --- diff --git a/src/muse.c b/src/muse.c index f31a7667f..bcb191f9f 100644 --- a/src/muse.c +++ b/src/muse.c @@ -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);