OSDN Git Service

update year to 2020
[jnethack/source.git] / src / pickup.c
index 2cc62d1..b2a6d39 100644 (file)
@@ -1,14 +1,15 @@
-/* NetHack 3.6 pickup.c        $NHDT-Date: 1445556881 2015/10/22 23:34:41 $  $NHDT-Branch: master $:$NHDT-Revision: 1.162 $ */
+/* NetHack 3.6 pickup.c        $NHDT-Date: 1576282488 2019/12/14 00:14:48 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.237 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Robert Patrick Rankin, 2012. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 /* JNetHack Copyright */
 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
-/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 /*
- *     Contains code for picking objects up, and container use.
+ *      Contains code for picking objects up, and container use.
  */
 
 #include "hack.h"
@@ -16,9 +17,9 @@
 #define CONTAINED_SYM '>' /* from invent.c */
 
 STATIC_DCL void FDECL(simple_look, (struct obj *, BOOLEAN_P));
-STATIC_DCL boolean
-FDECL(query_classes, (char *, boolean *, boolean *, const char *,
-                      struct obj *, BOOLEAN_P, int *));
+STATIC_DCL boolean FDECL(query_classes, (char *, boolean *, boolean *,
+                                         const char *, struct obj *,
+                                         BOOLEAN_P, int *));
 STATIC_DCL boolean FDECL(fatal_corpse_mistake, (struct obj *, BOOLEAN_P));
 STATIC_DCL void FDECL(check_here, (BOOLEAN_P));
 STATIC_DCL boolean FDECL(n_or_more, (struct obj *));
@@ -28,62 +29,56 @@ STATIC_DCL boolean FDECL(allow_cat_no_uchain, (struct obj *));
 #endif
 STATIC_DCL int FDECL(autopick, (struct obj *, int, menu_item **));
 STATIC_DCL int FDECL(count_categories, (struct obj *, int));
+STATIC_DCL int FDECL(delta_cwt, (struct obj *, struct obj *));
 STATIC_DCL long FDECL(carry_count, (struct obj *, struct obj *, long,
                                     BOOLEAN_P, int *, int *));
-STATIC_DCL int FDECL(lift_object,
-                     (struct obj *, struct obj *, long *, BOOLEAN_P));
+STATIC_DCL int FDECL(lift_object, (struct obj *, struct obj *, long *,
+                                   BOOLEAN_P));
 STATIC_DCL boolean FDECL(mbag_explodes, (struct obj *, int));
+STATIC_DCL long FDECL(boh_loss, (struct obj *container, int));
 STATIC_PTR int FDECL(in_container, (struct obj *));
 STATIC_PTR int FDECL(out_container, (struct obj *));
+STATIC_DCL void FDECL(removed_from_icebox, (struct obj *));
 STATIC_DCL long FDECL(mbag_item_gone, (int, struct obj *));
-STATIC_DCL void FDECL(observe_quantum_cat, (struct obj *));
-STATIC_DCL void NDECL(explain_container_prompt);
+STATIC_DCL void FDECL(explain_container_prompt, (BOOLEAN_P));
 STATIC_DCL int FDECL(traditional_loot, (BOOLEAN_P));
 STATIC_DCL int FDECL(menu_loot, (int, BOOLEAN_P));
 STATIC_DCL char FDECL(in_or_out_menu, (const char *, struct obj *, BOOLEAN_P,
-                                       BOOLEAN_P, BOOLEAN_P));
-STATIC_DCL int FDECL(container_at, (int, int, BOOLEAN_P));
+                                       BOOLEAN_P, BOOLEAN_P, BOOLEAN_P));
 STATIC_DCL boolean FDECL(able_to_loot, (int, int, BOOLEAN_P));
 STATIC_DCL boolean NDECL(reverse_loot);
 STATIC_DCL boolean FDECL(mon_beside, (int, int));
-STATIC_DCL int FDECL(do_loot_cont, (struct obj **));
+STATIC_DCL int FDECL(do_loot_cont, (struct obj **, int, int));
 STATIC_DCL void FDECL(tipcontainer, (struct obj *));
 
 /* define for query_objlist() and autopickup() */
 #define FOLLOW(curr, flags) \
-    (((flags) &BY_NEXTHERE) ? (curr)->nexthere : (curr)->nobj)
+    (((flags) & BY_NEXTHERE) ? (curr)->nexthere : (curr)->nobj)
 
-/*
- *  How much the weight of the given container will change when the given
- *  object is removed from it.  This calculation must match the one used
- *  by weight() in mkobj.c.
- */
-#define DELTA_CWT(cont, obj)                                      \
-    ((cont)->cursed ? (obj)->owt * 2 : (cont)->blessed            \
-                                           ? ((obj)->owt + 3) / 4 \
-                                           : ((obj)->owt + 1) / 2)
 #define GOLD_WT(n) (((n) + 50L) / 100L)
 /* if you can figure this out, give yourself a hearty pat on the back... */
 #define GOLD_CAPACITY(w, n) (((w) * -100L) - ((n) + 50L) - 1L)
 
-/* A variable set in use_container(), to be used by the callback routines  */
-/* in_container() and out_container() from askchain() and use_container(). */
-/* Also used by menu_loot() and container_gone().                         */
+/* A variable set in use_container(), to be used by the callback routines
+   in_container() and out_container() from askchain() and use_container().
+   Also used by menu_loot() and container_gone(). */
 static NEARDATA struct obj *current_container;
+static NEARDATA boolean abort_looting;
 #define Icebox (current_container->otyp == ICE_BOX)
 
+static const char
 /*JP
-static const char moderateloadmsg[] = "You have a little trouble lifting";
+        moderateloadmsg[] = "You have a little trouble lifting",
 */
-static const char moderateloadmsg[] = "\82ð\8e\9d\82Á\82½\82ç\8f­\82µ\82Ó\82ç\82Â\82¢\82½";
+        moderateloadmsg[] = "\82ð\8e\9d\82Á\82½\82ç\8f­\82µ\82Ó\82ç\82Â\82¢\82½",
 /*JP
-static const char nearloadmsg[] = "You have much trouble lifting";
+        nearloadmsg[] = "You have much trouble lifting",
 */
-static const char nearloadmsg[] = "\82Í\82¸\82Á\82µ\82è\82Æ\8c¨\82É\82Ì\82µ\82©\82©\82Á\82½";
+        nearloadmsg[] = "\82Í\82¸\82Á\82µ\82è\82Æ\8c¨\82É\82Ì\82µ\82©\82©\82Á\82½",
 /*JP
-static const char overloadmsg[] = "You have extreme difficulty lifting";
+        overloadmsg[] = "You have extreme difficulty lifting";
 */
-static const char overloadmsg[] = "\82ð\8e\9d\82¿\82 \82°\82é\82Ì\82Í\82Æ\82Ä\82à\82Â\82ç\82¢";
+        overloadmsg[] = "\82ð\8e\9d\82¿\82 \82°\82é\82Ì\82Í\82Æ\82Ä\82à\82Â\82ç\82¢";
 
 /* BUG: this lets you look at cockatrice corpses while blind without
    touching them */
@@ -103,6 +98,7 @@ boolean here;     /* flag for type of obj list linkage */
         pline1(doname(otmp));
     } else {
         winid tmpwin = create_nhwindow(NHW_MENU);
+
         putstr(tmpwin, 0, "");
         do {
             putstr(tmpwin, 0, doname(otmp));
@@ -139,15 +135,15 @@ int *itemcount;
 
 /*
  * Suppose some '?' and '!' objects are present, but '/' objects aren't:
- *     "a" picks all items without further prompting;
- *     "A" steps through all items, asking one by one;
- *     "?" steps through '?' items, asking, and ignores '!' ones;
- *     "/" becomes 'A', since no '/' present;
- *     "?a" or "a?" picks all '?' without further prompting;
- *     "/a" or "a/" becomes 'A' since there aren't any '/'
- *         (bug fix:  3.1.0 thru 3.1.3 treated it as "a");
- *     "?/a" or "a?/" or "/a?",&c picks all '?' even though no '/'
- *         (ie, treated as if it had just been "?a").
+ *      "a" picks all items without further prompting;
+ *      "A" steps through all items, asking one by one;
+ *      "?" steps through '?' items, asking, and ignores '!' ones;
+ *      "/" becomes 'A', since no '/' present;
+ *      "?a" or "a?" picks all '?' without further prompting;
+ *      "/a" or "a/" becomes 'A' since there aren't any '/'
+ *          (bug fix:  3.1.0 thru 3.1.3 treated it as "a");
+ *      "?/a" or "a?/" or "/a?",&c picks all '?' even though no '/'
+ *          (ie, treated as if it had just been "?a").
  */
 /*JP CHECK: 3.4.3 \82Å\82Ì\8cÄ\82Ñ\8fo\82µ\8c³
 pickup.c:572:("\8fE\82¤")   if (!query_classes(oclasses, &selective, &all_of_a_type,
@@ -164,45 +160,57 @@ struct obj *objs;
 boolean here;
 int *menu_on_demand;
 {
-    char ilets[30], inbuf[BUFSZ]; /* FIXME: hardcoded ilets[] length */
+    char ilets[36], inbuf[BUFSZ] = DUMMY; /* FIXME: hardcoded ilets[] length */
     int iletct, oclassct;
-    boolean not_everything;
+    boolean not_everything, filtered;
     char qbuf[QBUFSZ];
     boolean m_seen;
-    int itemcount;
+    int itemcount, bcnt, ucnt, ccnt, xcnt, ocnt;
 
     oclasses[oclassct = 0] = '\0';
     *one_at_a_time = *everything = m_seen = FALSE;
+    if (menu_on_demand)
+        *menu_on_demand = 0;
     iletct = collect_obj_classes(ilets, objs, here,
                                  (boolean FDECL((*), (OBJ_P))) 0, &itemcount);
-    if (iletct == 0) {
+    if (iletct == 0)
         return FALSE;
-    } else if (iletct == 1) {
+
+    if (iletct == 1) {
         oclasses[0] = def_char_to_objclass(ilets[0]);
         oclasses[1] = '\0';
-        if (itemcount && menu_on_demand) {
-            ilets[iletct++] = 'm';
-            *menu_on_demand = 0;
-            ilets[iletct] = '\0';
-        }
     } else { /* more than one choice available */
-        const char *where = 0;
-        register char sym, oc_of_sym, *p;
         /* additional choices */
         ilets[iletct++] = ' ';
         ilets[iletct++] = 'a';
         ilets[iletct++] = 'A';
         ilets[iletct++] = (objs == invent ? 'i' : ':');
-        if (menu_on_demand) {
-            ilets[iletct++] = 'm';
-            *menu_on_demand = 0;
-        }
-        ilets[iletct] = '\0';
-    ask_again:
+    }
+    if (itemcount && menu_on_demand)
+        ilets[iletct++] = 'm';
+    if (count_unpaid(objs))
+        ilets[iletct++] = 'u';
+
+    tally_BUCX(objs, here, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt);
+    if (bcnt)
+        ilets[iletct++] = 'B';
+    if (ucnt)
+        ilets[iletct++] = 'U';
+    if (ccnt)
+        ilets[iletct++] = 'C';
+    if (xcnt)
+        ilets[iletct++] = 'X';
+    ilets[iletct] = '\0';
+
+    if (iletct > 1) {
+        const char *where = 0;
+        char sym, oc_of_sym, *p;
+
+ ask_again:
         oclasses[oclassct = 0] = '\0';
         *one_at_a_time = *everything = FALSE;
-        not_everything = FALSE;
-#if 0 /*JP*/
+        not_everything = filtered = FALSE;
+#if 0 /*JP:T*/
         Sprintf(qbuf, "What kinds of thing do you want to %s? [%s]", action,
                 ilets);
 #else
@@ -213,8 +221,7 @@ int *menu_on_demand;
         if (*inbuf == '\033')
             return FALSE;
 
-        for (p = inbuf; (sym = *p++);) {
-            /* new A function (selective all) added by GAN 01/09/87 */
+        for (p = inbuf; (sym = *p++) != 0; ) {
             if (sym == ' ')
                 continue;
             else if (sym == 'A')
@@ -233,6 +240,9 @@ int *menu_on_demand;
                 goto ask_again;
             } else if (sym == 'm') {
                 m_seen = TRUE;
+            } else if (index("uBUCX", sym)) {
+                add_valid_menu_class(sym); /* 'u' or 'B','U','C',or 'X' */
+                filtered = TRUE;
             } else {
                 oc_of_sym = def_char_to_objclass(sym);
                 if (index(ilets, sym)) {
@@ -241,15 +251,10 @@ int *menu_on_demand;
                     oclasses[oclassct] = '\0';
                 } else {
                     if (!where)
-                        where =
-/*JP
-                            !strcmp(action, "pick up")
-*/
-                            !strcmp(action, "\8fE\82¤")
 /*JP
-                                ? "here"
+                        where = !strcmp(action, "pick up") ? "here"
 */
-                                ? "\82±\82±"
+                        where = !strcmp(action, "\8fE\82¤") ? "\82±\82±"
 /*JP
                                 : !strcmp(action, "take out") ? "inside" : "";
 */
@@ -267,9 +272,11 @@ int *menu_on_demand;
                     not_everything = TRUE;
                 }
             }
-        }
+        } /* for p:sym in inbuf */
+
         if (m_seen && menu_on_demand) {
-            *menu_on_demand = (*everything || !oclassct) ? -2 : -3;
+            *menu_on_demand = (((*everything || !oclassct) && !filtered)
+                               ? -2 : -3);
             return FALSE;
         }
         if (!oclassct && (!*everything || not_everything)) {
@@ -371,6 +378,14 @@ struct obj *obj;
 static char valid_menu_classes[MAXOCLASSES + 1 + 4 + 1];
 static boolean class_filter, bucx_filter, shop_filter;
 
+/* check valid_menu_classes[] for an entry; also used by askchain() */
+boolean
+menu_class_present(c)
+int c;
+{
+    return (c && index(valid_menu_classes, c)) ? TRUE : FALSE;
+}
+
 void
 add_valid_menu_class(c)
 int c;
@@ -380,7 +395,7 @@ int c;
     if (c == 0) { /* reset */
         vmc_count = 0;
         class_filter = bucx_filter = shop_filter = FALSE;
-    } else {
+    } else if (!menu_class_present(c)) {
         valid_menu_classes[vmc_count++] = (char) c;
         /* categorize the new class */
         switch (c) {
@@ -422,12 +437,25 @@ boolean
 allow_category(obj)
 struct obj *obj;
 {
-    /* unpaid and BUC checks don't apply to coins */
+    /* For coins, if any class filter is specified, accept if coins
+     * are included regardless of whether either unpaid or BUC-status
+     * is also specified since player has explicitly requested coins.
+     * If no class filtering is specified but bless/curse state is,
+     * coins are either unknown or uncursed based on an option setting.
+     */
     if (obj->oclass == COIN_CLASS)
-        return index(valid_menu_classes, COIN_CLASS) ? TRUE : FALSE;
+        return class_filter
+                 ? (index(valid_menu_classes, COIN_CLASS) ? TRUE : FALSE)
+                 : shop_filter /* coins are never unpaid, but check anyway */
+                    ? (obj->unpaid ? TRUE : FALSE)
+                    : bucx_filter
+                       ? (index(valid_menu_classes, iflags.goldX ? 'X' : 'U')
+                          ? TRUE : FALSE)
+                       : TRUE; /* catchall: no filters specified, so accept */
+
+    if (Role_if(PM_PRIEST) && !obj->bknown)
+        set_bknown(obj, 1);
 
-    if (Role_if(PM_PRIEST))
-        obj->bknown = TRUE;
     /*
      * There are three types of filters possible and the first and
      * third can have more than one entry:
@@ -444,6 +472,7 @@ struct obj *obj;
      * in accepting all scrolls and potions regardless of bless/curse
      * state plus all blessed non-scroll, non-potion objects.)
      */
+
     /* if class is expected but obj's class is not in the list, reject */
     if (class_filter && !index(valid_menu_classes, obj->oclass))
         return FALSE;
@@ -455,8 +484,8 @@ struct obj *obj;
     /* check for particular bless/curse state */
     if (bucx_filter) {
         /* first categorize this object's bless/curse state */
-        char bucx =
-            !obj->bknown ? 'X' : obj->blessed ? 'B' : obj->cursed ? 'C' : 'U';
+        char bucx = !obj->bknown ? 'X'
+                      : obj->blessed ? 'B' : obj->cursed ? 'C' : 'U';
 
         /* if its category is not in the list, reject */
         if (!index(valid_menu_classes, bucx))
@@ -473,11 +502,10 @@ allow_cat_no_uchain(obj)
 struct obj *obj;
 {
     if (obj != uchain
-       && ((index(valid_menu_classes,'u') && obj->unpaid)
+        && ((index(valid_menu_classes, 'u') && obj->unpaid)
             || index(valid_menu_classes, obj->oclass)))
-       return TRUE;
-    else
-       return FALSE;
+        return TRUE;
+    return FALSE;
 }
 #endif
 
@@ -486,8 +514,7 @@ boolean
 is_worn_by_type(otmp)
 register struct obj *otmp;
 {
-    return (boolean) (!!(otmp->owornmask & (W_ARMOR | W_ACCESSORY | W_WEAPON))
-                      && index(valid_menu_classes, otmp->oclass) != 0);
+    return (is_worn(otmp) && allow_category(otmp)) ? TRUE : FALSE;
 }
 
 /*
@@ -495,9 +522,9 @@ register struct obj *otmp;
  * or a monster's inventory if swallowed.
  *
  * Arg what:
- *     >0  autopickup
- *     =0  interactive
- *     <0  pickup count of something
+ *      >0  autopickup
+ *      =0  interactive
+ *      <0  pickup count of something
  *
  * Returns 1 if tried to pick something up, whether
  * or not it succeeded.
@@ -509,7 +536,7 @@ int what; /* should be a long */
     int i, n, res, count, n_tried = 0, n_picked = 0;
     menu_item *pick_list = (menu_item *) 0;
     boolean autopickup = what > 0;
-    struct obj *objchain;
+    struct obj **objchain_p;
     int traverse_how;
 
     /* we might have arrived here while fainted or sleeping, via
@@ -526,7 +553,8 @@ int what; /* should be a long */
         count = 0;
 
     if (!u.uswallow) {
-        struct trap *ttmp = t_at(u.ux, u.uy);
+        struct trap *ttmp;
+
         /* no auto-pick if no-pick move, nothing there, or in a pool */
         if (autopickup && (context.nopick || !OBJ_AT(u.ux, u.uy)
                            || (is_pool(u.ux, u.uy) && !Underwater)
@@ -534,11 +562,11 @@ int what; /* should be a long */
             read_engr_at(u.ux, u.uy);
             return 0;
         }
-
         /* no pickup if levitating & not on air or water level */
         if (!can_reach_floor(TRUE)) {
             if ((multi && !context.run) || (autopickup && !flags.pickup)
-                || (ttmp && uteetering_at_seen_pit(ttmp)))
+                || ((ttmp = t_at(u.ux, u.uy)) != 0
+                    && (uteetering_at_seen_pit(ttmp) || uescaped_shaft(ttmp))))
                 read_engr_at(u.ux, u.uy);
             return 0;
         }
@@ -569,10 +597,10 @@ int what; /* should be a long */
 
     add_valid_menu_class(0); /* reset */
     if (!u.uswallow) {
-        objchain = level.objects[u.ux][u.uy];
+        objchain_p = &level.objects[u.ux][u.uy];
         traverse_how = BY_NEXTHERE;
     } else {
-        objchain = u.ustuck->minvent;
+        objchain_p = &u.ustuck->minvent;
         traverse_how = 0; /* nobj */
     }
     /*
@@ -582,35 +610,40 @@ int what; /* should be a long */
      * to make things less confusing.
      */
     if (autopickup) {
-        n = autopick(objchain, traverse_how, &pick_list);
+        n = autopick(*objchain_p, traverse_how, &pick_list);
         goto menu_pickup;
     }
 
     if (flags.menu_style != MENU_TRADITIONAL || iflags.menu_requested) {
         /* use menus exclusively */
+        traverse_how |= AUTOSELECT_SINGLE
+                        | (flags.sortpack ? INVORDER_SORT : 0);
         if (count) { /* looking for N of something */
-            char buf[QBUFSZ];
+            char qbuf[QBUFSZ];
+
 /*JP
-            Sprintf(buf, "Pick %d of what?", count);
+            Sprintf(qbuf, "Pick %d of what?", count);
 */
-            Sprintf(buf, "\89½\82ð%d\8cÂ\8fE\82¢\82Ü\82·\82©\81H", count);
+            Sprintf(qbuf, "\89½\82ð%d\8cÂ\8fE\82¢\82Ü\82·\82©\81H", count);
             val_for_n_or_more = count; /* set up callback selector */
-            n = query_objlist(buf, objchain, traverse_how | AUTOSELECT_SINGLE
-                                                 | INVORDER_SORT,
+            n = query_objlist(qbuf, objchain_p, traverse_how,
                               &pick_list, PICK_ONE, n_or_more);
             /* correct counts, if any given */
             for (i = 0; i < n; i++)
                 pick_list[i].count = count;
         } else {
-/*JP
-            n = query_objlist("Pick up what?", objchain,
-*/
-            n = query_objlist("\89½\82ð\8fE\82¢\82Ü\82·\82©\81H", objchain,
-                              traverse_how | AUTOSELECT_SINGLE | INVORDER_SORT
-                                  | FEEL_COCKATRICE,
+#if 0 /*JP:T*/
+            n = query_objlist("Pick up what?", objchain_p,
+                              (traverse_how | FEEL_COCKATRICE),
+                              &pick_list, PICK_ANY, all_but_uchain);
+#else
+            n = query_objlist("\89½\82ð\8fE\82¢\82Ü\82·\82©\81H", objchain_p,
+                              (traverse_how | FEEL_COCKATRICE),
                               &pick_list, PICK_ANY, all_but_uchain);
+#endif
         }
-    menu_pickup:
+
+ menu_pickup:
         n_tried = n;
         for (n_picked = i = 0; i < n; i++) {
             res = pickup_object(pick_list[i].item.a_obj, pick_list[i].count,
@@ -626,8 +659,8 @@ int what; /* should be a long */
         /* old style interface */
         int ct = 0;
         long lcount;
-        boolean all_of_a_type, selective;
-        char oclasses[MAXOCLASSES];
+        boolean all_of_a_type, selective, bycat;
+        char oclasses[MAXOCLASSES + 10]; /* +10: room for B,U,C,X plus slop */
         struct obj *obj, *obj2;
 
         oclasses[0] = '\0';   /* types to consider (empty for all) */
@@ -635,13 +668,12 @@ int what; /* should be a long */
         selective = FALSE;    /* ask for each item */
 
         /* check for more than one object */
-        for (obj = objchain; obj;
-             obj = (traverse_how == BY_NEXTHERE) ? obj->nexthere : obj->nobj)
+        for (obj = *objchain_p; obj; obj = FOLLOW(obj, traverse_how))
             ct++;
 
         if (ct == 1 && count) {
             /* if only one thing, then pick it */
-            obj = objchain;
+            obj = *objchain_p;
             lcount = min(obj->quan, (long) count);
             n_tried++;
             if (pickup_object(obj, lcount, FALSE) > 0)
@@ -655,40 +687,50 @@ int what; /* should be a long */
             There("are %s objects here.", (ct <= 10) ? "several" : "many");
 */
             pline("\82±\82±\82É\82Í%s\82à\82Ì\82ª\82 \82é\81D", (ct <= 10) ? "\82¢\82­\82Â\82©" : "\91ò\8eR\82Ì");
+#if 0 /*JP:T*/
             if (!query_classes(oclasses, &selective, &all_of_a_type,
-/*JP
-                               "pick up", objchain,
-*/
-                               "\8fE\82¤", objchain,
-                               traverse_how == BY_NEXTHERE, &via_menu)) {
+                               "pick up", *objchain_p,
+                               (traverse_how & BY_NEXTHERE) ? TRUE : FALSE,
+                               &via_menu)) {
+#else
+            if (!query_classes(oclasses, &selective, &all_of_a_type,
+                               "\8fE\82¤", *objchain_p,
+                               (traverse_how & BY_NEXTHERE) ? TRUE : FALSE,
+                               &via_menu)) {
+#endif
                 if (!via_menu)
-                    return 0;
-                n = query_objlist(
-/*JP
-                    "Pick up what?", objchain,
-*/
-                    "\89½\82ð\8fE\82¢\82Ü\82·\82©\81H", objchain,
-                    traverse_how | (selective ? 0 : INVORDER_SORT),
-                    &pick_list, PICK_ANY,
-                    via_menu == -2 ? allow_all : allow_category);
+                    goto pickupdone;
+                if (selective)
+                    traverse_how |= INVORDER_SORT;
+#if 0 /*JP:T*/
+                n = query_objlist("Pick up what?", objchain_p, traverse_how,
+                                  &pick_list, PICK_ANY,
+                                  (via_menu == -2) ? allow_all
+                                                   : allow_category);
+#else
+                n = query_objlist("\89½\82ð\8fE\82¢\82Ü\82·\82©\81H", objchain_p, traverse_how,
+                                  &pick_list, PICK_ANY,
+                                  (via_menu == -2) ? allow_all
+                                                   : allow_category);
+#endif
                 goto menu_pickup;
             }
         }
-
-        for (obj = objchain; obj; obj = obj2) {
-            if (traverse_how == BY_NEXTHERE)
-                obj2 = obj->nexthere; /* perhaps obj will be picked up */
-            else
-                obj2 = obj->nobj;
-            lcount = -1L;
-
-            if (!selective && oclasses[0] && !index(oclasses, obj->oclass))
+        bycat = (menu_class_present('B') || menu_class_present('U')
+                 || menu_class_present('C') || menu_class_present('X'));
+
+        for (obj = *objchain_p; obj; obj = obj2) {
+            obj2 = FOLLOW(obj, traverse_how);
+            if (bycat ? !allow_category(obj)
+                      : (!selective && oclasses[0]
+                         && !index(oclasses, obj->oclass)))
                 continue;
 
+            lcount = -1L;
             if (!all_of_a_type) {
                 char qbuf[BUFSZ];
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 (void) safe_qbuf(qbuf, "Pick up ", "?", obj, doname,
                                  ansimpleoname, something);
 #else
@@ -714,7 +756,7 @@ int what; /* should be a long */
                     lcount = (long) yn_number;
                     if (lcount > obj->quan)
                         lcount = obj->quan;
-                /* fall thru */
+                    /*FALLTHRU*/
                 default: /* 'y' */
                     break;
                 }
@@ -727,8 +769,8 @@ int what; /* should be a long */
                 break;
             n_picked += res;
         }
   end_query:
-        ; /* semicolon needed by brain-damaged compilers */
+ end_query:
+        ; /* statement required after label */
     }
 
     if (!u.uswallow) {
@@ -737,33 +779,66 @@ int what; /* should be a long */
 
         /* position may need updating (invisible hero) */
         if (n_picked)
-            newsym(u.ux, u.uy);
+            newsym_force(u.ux, u.uy);
 
         /* check if there's anything else here after auto-pickup is done */
         if (autopickup)
             check_here(n_picked > 0);
     }
+ pickupdone:
+    add_valid_menu_class(0); /* reset */
     return (n_tried > 0);
 }
 
-boolean
-is_autopickup_exception(obj, grab)
+struct autopickup_exception *
+check_autopickup_exceptions(obj)
 struct obj *obj;
-boolean grab; /* forced pickup, rather than forced leave behind? */
 {
     /*
      *  Does the text description of this match an exception?
      */
-    char *objdesc = makesingular(doname(obj));
-    struct autopickup_exception *ape =
-        (grab) ? iflags.autopickup_exceptions[AP_GRAB]
-               : iflags.autopickup_exceptions[AP_LEAVE];
-    while (ape) {
-        if (regex_match(objdesc, ape->regex))
-            return TRUE;
-        ape = ape->next;
+    struct autopickup_exception *ape = apelist;
+
+    if (ape) {
+        char *objdesc = makesingular(doname(obj));
+
+        while (ape && !regex_match(objdesc, ape->regex))
+            ape = ape->next;
     }
-    return FALSE;
+    return ape;
+}
+
+boolean
+autopick_testobj(otmp, calc_costly)
+struct obj *otmp;
+boolean calc_costly;
+{
+    struct autopickup_exception *ape;
+    static boolean costly = FALSE;
+    const char *otypes = flags.pickup_types;
+    boolean pickit;
+
+    /* calculate 'costly' just once for a given autopickup operation */
+    if (calc_costly)
+        costly = (otmp->where == OBJ_FLOOR
+                  && costly_spot(otmp->ox, otmp->oy));
+
+    /* first check: reject if an unpaid item in a shop */
+    if (costly && !otmp->no_charge)
+        return FALSE;
+
+    /* check for pickup_types */
+    pickit = (!*otypes || index(otypes, otmp->oclass));
+
+    /* check for autopickup exceptions */
+    ape = check_autopickup_exceptions(otmp);
+    if (ape)
+        pickit = ape->grab;
+
+    /* pickup_thrown overrides pickup_types and exceptions */
+    if (!pickit)
+        pickit = (flags.pickup_thrown && otmp->was_thrown);
+    return pickit;
 }
 
 /*
@@ -782,37 +857,19 @@ menu_item **pick_list; /* list of objects and counts to pick up */
     menu_item *pi; /* pick item */
     struct obj *curr;
     int n;
-    boolean pickit;
-    const char *otypes = flags.pickup_types;
+    boolean check_costly = TRUE;
 
     /* first count the number of eligible items */
     for (n = 0, curr = olist; curr; curr = FOLLOW(curr, follow)) {
-        pickit = (!*otypes || index(otypes, curr->oclass));
-        /* check for "always pick up */
-        if (!pickit)
-            pickit = is_autopickup_exception(curr, TRUE);
-        /* then for "never pick up */
-        if (pickit)
-            pickit = !is_autopickup_exception(curr, FALSE);
-        /* pickup_thrown overrides pickup_types and exceptions */
-        if (!pickit)
-            pickit = (flags.pickup_thrown && curr->was_thrown);
-        /* finally, do we count this object? */
-        if (pickit)
+        if (autopick_testobj(curr, check_costly))
             ++n;
+        check_costly = FALSE; /* only need to check once per autopickup */
     }
 
     if (n) {
-        *pick_list = pi = (menu_item *) alloc(sizeof(menu_item) * n);
+        *pick_list = pi = (menu_item *) alloc(sizeof (menu_item) * n);
         for (n = 0, curr = olist; curr; curr = FOLLOW(curr, follow)) {
-            pickit = (!*otypes || index(otypes, curr->oclass));
-            if (!pickit)
-                pickit = is_autopickup_exception(curr, TRUE);
-            if (pickit)
-                pickit = !is_autopickup_exception(curr, FALSE);
-            if (!pickit)
-                pickit = (flags.pickup_thrown && curr->was_thrown);
-            if (pickit) {
+            if (autopick_testobj(curr, FALSE)) {
                 pi[n].item.a_obj = curr;
                 pi[n].count = curr->quan;
                 n++;
@@ -830,33 +887,37 @@ menu_item **pick_list; /* list of objects and counts to pick up */
  * returned counts are guaranteed to be in bounds and non-zero.
  *
  * Query flags:
- *     BY_NEXTHERE       - Follow object list via nexthere instead of nobj.
- *     AUTOSELECT_SINGLE - Don't ask if only 1 object qualifies - just
- *                         use it.
- *     USE_INVLET        - Use object's invlet.
- *     INVORDER_SORT     - Use hero's pack order.
- *     INCLUDE_HERO      - Showing engulfer's invent; show hero too.
- *     SIGNAL_NOMENU     - Return -1 rather than 0 if nothing passes "allow".
- *     SIGNAL_ESCAPE     - Return -1 rather than 0 if player uses ESC to
- *                         pick nothing.
+ *      BY_NEXTHERE       - Follow object list via nexthere instead of nobj.
+ *      AUTOSELECT_SINGLE - Don't ask if only 1 object qualifies - just
+ *                          use it.
+ *      USE_INVLET        - Use object's invlet.
+ *      INVORDER_SORT     - Use hero's pack order.
+ *      INCLUDE_HERO      - Showing engulfer's invent; show hero too.
+ *      SIGNAL_NOMENU     - Return -1 rather than 0 if nothing passes "allow".
+ *      SIGNAL_ESCAPE     - Return -1 rather than 0 if player uses ESC to
+ *                          pick nothing.
+ *      FEEL_COCKATRICE   - touch corpse.
  */
 int
-query_objlist(qstr, olist, qflags, pick_list, how, allow)
+query_objlist(qstr, olist_p, qflags, pick_list, how, allow)
 const char *qstr;                 /* query string */
-struct obj *olist;                /* the list to pick from */
+struct obj **olist_p;             /* the list to pick from */
 int qflags;                       /* options to control the query */
 menu_item **pick_list;            /* return list of items picked */
 int how;                          /* type of query */
 boolean FDECL((*allow), (OBJ_P)); /* allow function */
 {
-    int i, n, actualn;
+    int i, n;
     winid win;
-    struct obj *curr, *last, fake_hero_object;
-    struct obj **oarray;
+    struct obj *curr, *last, fake_hero_object, *olist = *olist_p;
     char *pack;
     anything any;
-    boolean printed_type_name, sorted = (qflags & INVORDER_SORT) != 0,
-                               engulfer = (qflags & INCLUDE_HERO) != 0;
+    boolean printed_type_name, first,
+            sorted = (qflags & INVORDER_SORT) != 0,
+            engulfer = (qflags & INCLUDE_HERO) != 0,
+            engulfer_minvent;
+    unsigned sortflags;
+    Loot *sortedolist, *srtoli;
 
     *pick_list = (menu_item *) 0;
     if (!olist && !engulfer)
@@ -868,7 +929,13 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
             last = curr;
             n++;
         }
-    actualn = n;
+    /* can't depend upon 'engulfer' because that's used to indicate whether
+       hero should be shown as an extra, fake item */
+    engulfer_minvent = (olist && olist->where == OBJ_MINVENT
+                        && u.uswallow && olist->ocarry == u.ustuck);
+    if (engulfer_minvent && n == 1 && olist->owornmask != 0L) {
+        qflags &= ~AUTOSELECT_SINGLE;
+    }
     if (engulfer) {
         ++n;
         /* don't autoselect swallowed hero if it's the only choice */
@@ -879,73 +946,75 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
         return (qflags & SIGNAL_NOMENU) ? -1 : 0;
 
     if (n == 1 && (qflags & AUTOSELECT_SINGLE)) {
-        *pick_list = (menu_item *) alloc(sizeof(menu_item));
+        *pick_list = (menu_item *) alloc(sizeof (menu_item));
         (*pick_list)->item.a_obj = last;
         (*pick_list)->count = last->quan;
         return 1;
     }
 
-    oarray = objarr_init(actualn);
-    /* Add objects to the array */
-    i = 0;
-    for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
-        if ((*allow)(curr)) {
-            objarr_set(curr, i++, oarray, (flags.sortloot == 'f'
-                                           || (flags.sortloot == 'l'
-                                               && !(qflags & USE_INVLET))));
-        }
-    }
+    sortflags = (((flags.sortloot == 'f'
+                   || (flags.sortloot == 'l' && !(qflags & USE_INVLET)))
+                  ? SORTLOOT_LOOT
+                  : ((qflags & USE_INVLET) ? SORTLOOT_INVLET : 0))
+                 | (flags.sortpack ? SORTLOOT_PACK : 0)
+                 | ((qflags & FEEL_COCKATRICE) ? SORTLOOT_PETRIFY : 0));
+    sortedolist = sortloot(&olist, sortflags,
+                           (qflags & BY_NEXTHERE) ? TRUE : FALSE, allow);
 
     win = create_nhwindow(NHW_MENU);
     start_menu(win);
     any = zeroany;
-
     /*
      * Run through the list and add the objects to the menu.  If
      * INVORDER_SORT is set, we'll run through the list once for
-     * each type so we can group them.  The allow function will only
-     * be called once per object in the list.
+     * each type so we can group them.  The allow function was
+     * called by sortloot() and will be called once per item here.
      */
     pack = flags.inv_order;
+    first = TRUE;
     do {
         printed_type_name = FALSE;
-        for (i = 0; i < actualn; i++) {
-            curr = oarray[i];
+        for (srtoli = sortedolist; ((curr = srtoli->obj) != 0); ++srtoli) {
+            if (sorted && curr->oclass != *pack)
+                continue;
             if ((qflags & FEEL_COCKATRICE) && curr->otyp == CORPSE
                 && will_feel_cockatrice(curr, FALSE)) {
                 destroy_nhwindow(win); /* stop the menu and revert */
                 (void) look_here(0, FALSE);
+                unsortloot(&sortedolist);
                 return 0;
             }
-            if ((!sorted || curr->oclass == *pack) && (*allow)(curr)) {
+            if ((*allow)(curr)) {
                 /* if sorting, print type name (once only) */
                 if (sorted && !printed_type_name) {
                     any = zeroany;
                     add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
                              let_to_name(*pack, FALSE,
-                                         (how != PICK_NONE)
-                                             && iflags.menu_head_objsym),
+                                         ((how != PICK_NONE)
+                                          && iflags.menu_head_objsym)),
                              MENU_UNSELECTED);
                     printed_type_name = TRUE;
                 }
 
                 any.a_obj = curr;
-                add_menu(win, obj_to_glyph(curr), &any,
-                         (qflags & USE_INVLET) ? curr->invlet : 0,
+                add_menu(win, obj_to_glyph(curr, rn2_on_display_rng), &any,
+                         (qflags & USE_INVLET) ? curr->invlet
+                           : (first && curr->oclass == COIN_CLASS) ? '$' : 0,
                          def_oc_syms[(int) objects[curr->otyp].oc_class].sym,
                          ATR_NONE, doname_with_price(curr), MENU_UNSELECTED);
+                first = FALSE;
             }
         }
         pack++;
     } while (sorted && *pack);
-    free(oarray);
+    unsortloot(&sortedolist);
 
     if (engulfer) {
         char buf[BUFSZ];
 
         any = zeroany;
         if (sorted && n > 1) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             Sprintf(buf, "%s Creatures",
                     is_animal(u.ustuck->data) ? "Swallowed" : "Engulfed");
 #else
@@ -958,7 +1027,7 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
         fake_hero_object = zeroobj;
         fake_hero_object.quan = 1L; /* not strictly necessary... */
         any.a_obj = &fake_hero_object;
-        add_menu(win, mon_to_glyph(&youmonst), &any,
+        add_menu(win, mon_to_glyph(&youmonst, rn2_on_display_rng), &any,
                  /* fake inventory letter, no group accelerator */
                  CONTAINED_SYM, 0, ATR_NONE, an(self_lookat(buf)),
                  MENU_UNSELECTED);
@@ -975,10 +1044,26 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
         /* fix up counts:  -1 means no count used => pick all;
            if fake_hero_object was picked, discard that choice */
         for (i = k = 0, mi = *pick_list; i < n; i++, mi++) {
-            if (mi->item.a_obj == &fake_hero_object)
+            curr = mi->item.a_obj;
+            if (curr == &fake_hero_object) {
+                /* this isn't actually possible; fake item representing
+                   hero is only included for look here (':'), not pickup,
+                   and that's PICK_NONE so we can't get here from there */
+/*JP
+                You_cant("pick yourself up!");
+*/
+                You_cant("\8e©\95ª\8e©\90g\82Í\8fE\82¦\82È\82¢\81I");
                 continue;
-            if (mi->count == -1L || mi->count > mi->item.a_obj->quan)
-                mi->count = mi->item.a_obj->quan;
+            }
+            if (engulfer_minvent && curr->owornmask != 0L) {
+/*JP
+                You_cant("pick %s up.", ysimple_name(curr));
+*/
+                You_cant("%s\82ð\8fE\82¦\82È\82¢\81D", ysimple_name(curr));
+                continue;
+            }
+            if (mi->count == -1L || mi->count > curr->quan)
+                mi->count = curr->quan;
             if (k < i)
                 (*pick_list)[k] = *mi;
             ++k;
@@ -989,10 +1074,14 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
             *pick_list = 0;
             n = 0;
         } else if (k < n) {
-            /* other stuff plus fake_hero; last slot is now unused */
-            (*pick_list)[k].item = zeroany;
-            (*pick_list)[k].count = 0L;
-            n = k;
+            /* other stuff plus fake_hero; last slot is now unused
+               (could be more than one if player tried to pick items
+               worn by engulfer) */
+            while (n > k) {
+                --n;
+                (*pick_list)[n].item = zeroany;
+                (*pick_list)[n].count = 0L;
+            }
         }
     } else if (n < 0) {
         /* -1 is used for SIGNAL_NOMENU, so callers don't expect it
@@ -1022,6 +1111,7 @@ int how;               /* type of query */
     boolean collected_type_name;
     char invlet;
     int ccount;
+    boolean FDECL((*ofilter), (OBJ_P)) = (boolean FDECL((*), (OBJ_P))) 0;
     boolean do_unpaid = FALSE;
     boolean do_blessed = FALSE, do_cursed = FALSE, do_uncursed = FALSE,
             do_buc_unknown = FALSE;
@@ -1032,19 +1122,21 @@ int how;               /* type of query */
         return 0;
     if ((qflags & UNPAID_TYPES) && count_unpaid(olist))
         do_unpaid = TRUE;
-    if ((qflags & BUC_BLESSED) && count_buc(olist, BUC_BLESSED)) {
+    if (qflags & WORN_TYPES)
+        ofilter = is_worn;
+    if ((qflags & BUC_BLESSED) && count_buc(olist, BUC_BLESSED, ofilter)) {
         do_blessed = TRUE;
         num_buc_types++;
     }
-    if ((qflags & BUC_CURSED) && count_buc(olist, BUC_CURSED)) {
+    if ((qflags & BUC_CURSED) && count_buc(olist, BUC_CURSED, ofilter)) {
         do_cursed = TRUE;
         num_buc_types++;
     }
-    if ((qflags & BUC_UNCURSED) && count_buc(olist, BUC_UNCURSED)) {
+    if ((qflags & BUC_UNCURSED) && count_buc(olist, BUC_UNCURSED, ofilter)) {
         do_uncursed = TRUE;
         num_buc_types++;
     }
-    if ((qflags & BUC_UNKNOWN) && count_buc(olist, BUC_UNKNOWN)) {
+    if ((qflags & BUC_UNKNOWN) && count_buc(olist, BUC_UNKNOWN, ofilter)) {
         do_buc_unknown = TRUE;
         num_buc_types++;
     }
@@ -1054,24 +1146,45 @@ int how;               /* type of query */
     if (ccount == 1 && !do_unpaid && num_buc_types <= 1
         && !(qflags & BILLED_TYPES)) {
         for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
-            if ((qflags & WORN_TYPES)
-                && !(curr->owornmask & (W_ARMOR | W_ACCESSORY | W_WEAPON)))
+            if (ofilter && !(*ofilter)(curr))
                 continue;
             break;
         }
         if (curr) {
             *pick_list = (menu_item *) alloc(sizeof(menu_item));
             (*pick_list)->item.a_int = curr->oclass;
-            return 1;
+            n = 1;
         } else {
             debugpline0("query_category: no single object match");
+            n = 0;
         }
-        return 0;
+        /* early return is ok; there's no temp window yet */
+        return n;
     }
 
     win = create_nhwindow(NHW_MENU);
     start_menu(win);
     pack = flags.inv_order;
+
+    if (qflags & CHOOSE_ALL) {
+        invlet = 'A';
+        any = zeroany;
+        any.a_int = 'A';
+        add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
+/*JP
+                 (qflags & WORN_TYPES) ? "Auto-select every item being worn"
+*/
+                 (qflags & WORN_TYPES) ? "\90g\82É\82Â\82¯\82ç\82ê\82é\95¨\91S\82Ä"
+/*JP
+                                       : "Auto-select every item",
+*/
+                                       : "\91S\82Ä",
+                 MENU_UNSELECTED);
+
+        any = zeroany;
+        add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
+    }
+
     if ((qflags & ALL_TYPES) && (ccount > 1)) {
         invlet = 'a';
         any = zeroany;
@@ -1089,8 +1202,7 @@ int how;               /* type of query */
         collected_type_name = FALSE;
         for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
             if (curr->oclass == *pack) {
-                if ((qflags & WORN_TYPES)
-                    && !(curr->owornmask & (W_ARMOR | W_ACCESSORY | W_WEAPON)))
+                if (ofilter && !(*ofilter)(curr))
                     continue;
                 if (!collected_type_name) {
                     any = zeroany;
@@ -1109,9 +1221,17 @@ int how;               /* type of query */
         pack++;
         if (invlet >= 'u') {
             impossible("query_category: too many categories");
-            return 0;
+            n = 0;
+            goto query_done;
         }
     } while (*pack);
+
+    if (do_unpaid || (qflags & BILLED_TYPES) || do_blessed || do_cursed
+        || do_uncursed || do_buc_unknown) {
+        any = zeroany;
+        add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
+    }
+
     /* unpaid items if there are any */
     if (do_unpaid) {
         invlet = 'u';
@@ -1134,22 +1254,10 @@ int how;               /* type of query */
 */
                  "\96¢\95¥\82Å\8eg\82Á\82Ä\82µ\82Ü\82Á\82½\82à\82Ì", MENU_UNSELECTED);
     }
-    if (qflags & CHOOSE_ALL) {
-        invlet = 'A';
-        any = zeroany;
-        any.a_int = 'A';
-        add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
-/*JP
-                 (qflags & WORN_TYPES) ? "Auto-select every item being worn"
-*/
-                 (qflags & WORN_TYPES) ? "\90g\82É\82Â\82¯\82ç\82ê\82é\95¨\91S\82Ä"
-/*JP
-                                       : "Auto-select every item",
-*/
-                                       : "\91S\82Ä",
-                 MENU_UNSELECTED);
-    }
-    /* items with b/u/c/unknown if there are any */
+
+    /* items with b/u/c/unknown if there are any;
+       this cluster of menu entries is in alphabetical order,
+       reversing the usual sequence of 'U' and 'C' in BUCX */
     if (do_blessed) {
         invlet = 'B';
         any = zeroany;
@@ -1184,14 +1292,17 @@ int how;               /* type of query */
         invlet = 'X';
         any = zeroany;
         any.a_int = 'X';
+#if 0 /*JP:T*/
+        add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
+                 "Items of unknown Bless/Curse status", MENU_UNSELECTED);
+#else
         add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
-/*JP
-                 "Items of unknown B/C/U status", MENU_UNSELECTED);
-*/
                  "\8fj\95\9f\81^\8eô\82¢\82ª\82í\82©\82ç\82È\82¢\82à\82Ì", MENU_UNSELECTED);
+#endif
     }
     end_menu(win, qstr);
     n = select_menu(win, how, pick_list);
+ query_done:
     destroy_nhwindow(win);
     if (n < 0)
         n = 0; /* caller's don't expect -1 */
@@ -1214,7 +1325,8 @@ int qflags;
         for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
             if (curr->oclass == *pack) {
                 if ((qflags & WORN_TYPES)
-                    && !(curr->owornmask & (W_ARMOR | W_ACCESSORY | W_WEAPON)))
+                    && !(curr->owornmask & (W_ARMOR | W_ACCESSORY
+                                            | W_WEAPONS)))
                     continue;
                 if (!counted_category) {
                     ccount++;
@@ -1227,6 +1339,37 @@ int qflags;
     return ccount;
 }
 
+/*
+ *  How much the weight of the given container will change when the given
+ *  object is removed from it.  Use before and after weight amounts rather
+ *  than trying to match the calculation used by weight() in mkobj.c.
+ */
+STATIC_OVL int
+delta_cwt(container, obj)
+struct obj *container, *obj;
+{
+    struct obj **prev;
+    int owt, nwt;
+
+    if (container->otyp != BAG_OF_HOLDING)
+        return obj->owt;
+
+    owt = nwt = container->owt;
+    /* find the object so that we can remove it */
+    for (prev = &container->cobj; *prev; prev = &(*prev)->nobj)
+        if (*prev == obj)
+            break;
+    if (!*prev) {
+        panic("delta_cwt: obj not inside container?");
+    } else {
+        /* temporarily remove the object and calculate resulting weight */
+        *prev = obj->nobj;
+        nwt = weight(container);
+        *prev = obj; /* put the object back; obj->nobj is still valid */
+    }
+    return owt - nwt;
+}
+
 /* could we carry `obj'? if not, could we carry some of it/them? */
 STATIC_OVL long
 carry_count(obj, container, count, telekinesis, wt_before, wt_after)
@@ -1258,9 +1401,7 @@ int *wt_before, *wt_after;
     }
     wt = iw + (int) obj->owt;
     if (adjust_wt)
-        wt -= (container->otyp == BAG_OF_HOLDING)
-                  ? (int) DELTA_CWT(container, obj)
-                  : (int) obj->owt;
+        wt -= delta_cwt(container, obj);
     /* This will go with silver+copper & new gold weight */
     if (is_gold) /* merged gold might affect cumulative weight */
         wt -= (GOLD_WT(umoney) + GOLD_WT(count) - GOLD_WT(umoney + count));
@@ -1288,9 +1429,7 @@ int *wt_before, *wt_after;
                 obj->quan = qq;
                 obj->owt = (unsigned) GOLD_WT(qq);
                 ow = (int) GOLD_WT(umoney + qq);
-                ow -= (container->otyp == BAG_OF_HOLDING)
-                          ? (int) DELTA_CWT(container, obj)
-                          : (int) obj->owt;
+                ow -= delta_cwt(container, obj);
                 if (iw + ow >= 0)
                     break;
                 oow = ow;
@@ -1309,15 +1448,13 @@ int *wt_before, *wt_after;
          * object and calling weight.
          *
          * This works for containers only because containers
-         * don't merge.                -dean
+         * don't merge.  -dean
          */
         for (qq = 1L; qq <= count; qq++) {
             obj->quan = qq;
             obj->owt = (unsigned) (ow = weight(obj));
             if (adjust_wt)
-                ow -= (container->otyp == BAG_OF_HOLDING)
-                          ? (int) DELTA_CWT(container, obj)
-                          : (int) obj->owt;
+                ow -= delta_cwt(container, obj);
             if (iw + ow >= 0)
                 break;
             wt = iw + ow;
@@ -1360,7 +1497,7 @@ int *wt_before, *wt_after;
     /* we can carry qq of them */
     if (qq > 0) {
         if (qq < count)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("can only %s %s of the %s %s.", verb,
                 (qq == 1L) ? "one" : "some", obj_nambuf, where);
 #else
@@ -1372,7 +1509,7 @@ int *wt_before, *wt_after;
     }
 
     if (!container)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Strcpy(where, "here"); /* slightly shorter form */
 #else
         Strcpy(where, "\82±\82±\82É\82Í");  /* slightly shorter form */
@@ -1417,7 +1554,7 @@ boolean telekinesis;
     int result, old_wt, new_wt, prev_encumbr, next_encumbr;
 
     if (obj->otyp == BOULDER && Sokoban) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("cannot get your %s around this %s.", body_part(HAND),
             xname(obj));
 #else
@@ -1436,7 +1573,7 @@ boolean telekinesis;
             return 1; /* lift regardless of current situation */
         /* if we reach here, we're out of slots and already have at least
            one of these, so treat this one more like a normal item */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("are carrying too much stuff to pick up %s %s.",
             (obj->quan == 1L) ? "another" : "more", simpleonames(obj));
 #else
@@ -1446,18 +1583,29 @@ boolean telekinesis;
         return -1;
     }
 
-    *cnt_p =
-        carry_count(obj, container, *cnt_p, telekinesis, &old_wt, &new_wt);
+    *cnt_p = carry_count(obj, container, *cnt_p, telekinesis,
+                         &old_wt, &new_wt);
     if (*cnt_p < 1L) {
         result = -1; /* nothing lifted */
     } else if (obj->oclass != COIN_CLASS
                /* [exception for gold coins will have to change
                    if silver/copper ones ever get implemented] */
                && inv_cnt(FALSE) >= 52 && !merge_choice(invent, obj)) {
-/*JP
-        Your("knapsack cannot accommodate any more items.");
-*/
-        Your("\83i\83b\83v\83U\83b\83N\82Í\82±\82ê\88È\8fã\83A\83C\83e\83\80\82ð\8bl\82ß\8d\9e\82ß\82È\82¢\81D");
+        /* if there is some gold here (and we haven't already skipped it),
+           we aren't limited by the 52 item limit for it, but caller and
+           "grandcaller" aren't prepared to skip stuff and then pickup
+           just gold, so the best we can do here is vary the message */
+#if 0 /*JP*/
+        Your("knapsack cannot accommodate any more items%s.",
+             /* floor follows by nexthere, otherwise container so by nobj */
+             nxtobj(obj, GOLD_PIECE, (boolean) (obj->where == OBJ_FLOOR))
+                 ? " (except gold)" : "");
+#else
+        Your("\83i\83b\83v\83U\83b\83N\82Í%s\82±\82ê\88È\8fã\83A\83C\83e\83\80\82ð\8bl\82ß\8d\9e\82ß\82È\82¢\81D",
+             /* floor follows by nexthere, otherwise container so by nobj */
+             nxtobj(obj, GOLD_PIECE, (boolean) (obj->where == OBJ_FLOOR))
+                 ? "(\8bà\89Ý\88È\8aO)" : "");
+#endif
         result = -1; /* nothing lifted */
     } else {
         result = 1;
@@ -1551,6 +1699,13 @@ boolean telekinesis; /* not picking it up directly by hand */
 
     if (obj == uchain) { /* do not pick up attached chain */
         return 0;
+    } else if (obj->where == OBJ_MINVENT && obj->owornmask != 0L
+               && u.uswallow && obj->ocarry == u.ustuck) {
+/*JP
+        You_cant("pick %s up.", ysimple_name(obj));
+*/
+        You_cant("%s\82ð\8fE\82¦\82È\82¢\81D", ysimple_name(obj));
+        return 0;
     } else if (obj->oartifact && !touch_artifact(obj, &youmonst)) {
         return 0;
     } else if (obj->otyp == CORPSE) {
@@ -1563,7 +1718,7 @@ boolean telekinesis; /* not picking it up directly by hand */
         else if (!obj->spe && !obj->cursed)
             obj->spe = 1;
         else {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline_The("scroll%s %s to dust as you %s %s up.", plur(obj->quan),
                       otense(obj, "turn"), telekinesis ? "raise" : "pick",
                       (obj->quan == 1L) ? "it" : "them");
@@ -1610,26 +1765,43 @@ struct obj *
 pick_obj(otmp)
 struct obj *otmp;
 {
+    struct obj *result;
+    int ox = otmp->ox, oy = otmp->oy;
+    boolean robshop = (!u.uswallow && otmp != uball && costly_spot(ox, oy));
+
     obj_extract_self(otmp);
-    if (!u.uswallow && otmp != uball && costly_spot(otmp->ox, otmp->oy)) {
+    newsym(ox, oy);
+
+    /* for shop items, addinv() needs to be after addtobill() (so that
+       object merger can take otmp->unpaid into account) but before
+       remote_robbery() (which calls rob_shop() which calls setpaid()
+       after moving costs of unpaid items to shop debt; setpaid()
+       calls clear_unpaid() for lots of object chains, but 'otmp' isn't
+       on any of those between obj_extract_self() and addinv(); for
+       3.6.0, 'otmp' remained flagged as an unpaid item in inventory
+       and triggered impossible() every time inventory was examined) */
+    if (robshop) {
         char saveushops[5], fakeshop[2];
 
         /* addtobill cares about your location rather than the object's;
            usually they'll be the same, but not when using telekinesis
            (if ever implemented) or a grappling hook */
         Strcpy(saveushops, u.ushops);
-        fakeshop[0] = *in_rooms(otmp->ox, otmp->oy, SHOPBASE);
+        fakeshop[0] = *in_rooms(ox, oy, SHOPBASE);
         fakeshop[1] = '\0';
         Strcpy(u.ushops, fakeshop);
         /* sets obj->unpaid if necessary */
         addtobill(otmp, TRUE, FALSE, FALSE);
         Strcpy(u.ushops, saveushops);
-        /* if you're outside the shop, make shk notice */
-        if (!index(u.ushops, *fakeshop))
-            remote_burglary(otmp->ox, otmp->oy);
+        robshop = otmp->unpaid && !index(u.ushops, *fakeshop);
     }
-    newsym(otmp->ox, otmp->oy);
-    return addinv(otmp); /* might merge it with other objects */
+
+    result = addinv(otmp);
+    /* if you're taking a shop item from outside the shop, make shk notice */
+    if (robshop)
+        remote_burglary(ox, oy);
+
+    return result;
 }
 
 /*
@@ -1657,15 +1829,15 @@ encumber_msg()
             You("\89×\95¨\82Ì\92Þ\8d\87\82ð\82Æ\82è\92¼\82µ\82½\82ª\81C\93®\82«\82É\82­\82¢\81D");
             break;
         case 3:
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("%s under your heavy load.  Movement is very hard.",
                 stagger(youmonst.data, "stagger"));
 #else
-            You("\89×\95¨\82Ì\8fd\82Ý\82Å\82Ì\82½\82¤\82¿\82Ü\82í\82Á\82½\81D\93®\82­\82Ì\82ª\94ñ\8fí\82É\82«\82Â\82¢\81D");
+            You("\89×\95¨\82Ì\8fd\82Ý\82Å\82æ\82ë\82æ\82ë\82µ\82½\81D\93®\82­\82Ì\82ª\94ñ\8fí\82É\82«\82Â\82¢\81D");
 #endif
             break;
         default:
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("%s move a handspan with this load!",
                 newcap == 4 ? "can barely" : "can't even");
 #else
@@ -1695,11 +1867,11 @@ encumber_msg()
             You("\89×\95¨\82Ì\92Þ\8d\87\82ð\82Æ\82è\92¼\82µ\82½\81D\82¾\82ª\82Ü\82¾\93®\82­\82Ì\82Í\82«\82Â\82¢\81D");
             break;
         case 3:
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("%s under your load.  Movement is still very hard.",
                 stagger(youmonst.data, "stagger"));
 #else
-            You("\89×\95¨\82Ì\8fd\82Ý\82ª\82¸\82Á\82µ\82è\82Æ\8c¨\82É\82­\82é\81D\82Ü\82¾\93®\82­\82Ì\82ª\94ñ\8fí\82É\82«\82Â\82¢\81D");
+            You("\89×\95¨\82Ì\8fd\82Ý\82ª\82¸\82Á\82µ\82è\82Æ\82­\82é\81D\82Ü\82¾\93®\82­\82Ì\82ª\94ñ\8fí\82É\82«\82Â\82¢\81D");
 #endif
             break;
         }
@@ -1711,7 +1883,7 @@ encumber_msg()
 }
 
 /* Is there a container at x,y. Optional: return count of containers at x,y */
-STATIC_OVL int
+int
 container_at(x, y, countem)
 int x, y;
 boolean countem;
@@ -1749,12 +1921,12 @@ boolean looting; /* loot vs tip */
     } else if ((is_pool(x, y) && (looting || !Underwater)) || is_lava(x, y)) {
         /* at present, can't loot in water even when Underwater;
            can tip underwater, but not when over--or stuck in--lava */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("cannot %s things that are deep in the %s.", verb,
-            is_lava(x, y) ? "lava" : "water");
+            hliquid(is_lava(x, y) ? "lava" : "water"));
 #else
         You("%s\82É\90[\82­\82É\92¾\82ñ\82¾\82à\82Ì\82ð%s\82±\82Æ\82Í\82Å\82«\82È\82¢\81D",
-            is_lava(u.ux, u.uy) ? "\97n\8aâ" : "\90\85", verb);
+            hliquid(is_lava(u.ux, u.uy) ? "\97n\8aâ" : "\90\85"), verb);
 #endif
         return FALSE;
     } else if (nolimbs(youmonst.data)) {
@@ -1779,6 +1951,7 @@ mon_beside(x, y)
 int x, y;
 {
     int i, j, nx, ny;
+
     for (i = -1; i <= 1; i++)
         for (j = -1; j <= 1; j++) {
             nx = x + i;
@@ -1789,31 +1962,46 @@ int x, y;
     return FALSE;
 }
 
-int
-do_loot_cont(cobjp)
+STATIC_OVL int
+do_loot_cont(cobjp, cindex, ccount)
 struct obj **cobjp;
+int cindex, ccount; /* index of this container (1..N), number of them (N) */
 {
     struct obj *cobj = *cobjp;
+
     if (!cobj)
         return 0;
     if (cobj->olocked) {
-#if 0 /*JP*/
-        pline("%s locked.",
-              cobj->lknown ? "It is" : "Hmmm, it turns out to be");
+        if (ccount < 2)
+#if 0 /*JP:T*/
+            pline("%s locked.",
+                  cobj->lknown ? "It is" : "Hmmm, it turns out to be");
 #else
-        pline("%s\8c®\82ª\82©\82©\82Á\82Ä\82¢\82é\81D", cobj->lknown ? "" : "\82Þ\81[\82ñ\81C");
+            pline("%s\8c®\82ª\82©\82©\82Á\82Ä\82¢\82é\81D",
+                  cobj->lknown ? "" : "\82Þ\81[\82ñ\81C");
 #endif
+        else if (cobj->lknown)
+/*JP
+            pline("%s is locked.", The(xname(cobj)));
+*/
+            pline("%s\82Í\8c®\82ª\82©\82©\82Á\82Ä\82¢\82é\81D", xname(cobj));
+        else
+/*JP
+            pline("Hmmm, %s turns out to be locked.", the(xname(cobj)));
+*/
+            pline("\82Þ\81[\82ñ\81C%s\82Í\8c®\82ª\82©\82©\82Á\82Ä\82¢\82é\81D", xname(cobj));
         cobj->lknown = 1;
         return 0;
     }
-    cobj->lknown = 1;
+    cobj->lknown = 1; /* floor container, so no need for update_inventory() */
 
     if (cobj->otyp == BAG_OF_TRICKS) {
         int tmp;
+
 /*JP
-        You("carefully open the bag...");
+        You("carefully open %s...", the(xname(cobj)));
 */
-        You("\90T\8fd\82É\8a\93\82ð\8aJ\82¯\82½\81D\81D\81D");
+        You("\90T\8fd\82É%s\82ð\8aJ\82¯\82½\81D\81D\81D", xname(cobj));
 /*JP
         pline("It develops a huge set of teeth and bites you!");
 */
@@ -1824,6 +2012,7 @@ struct obj **cobjp;
 */
         losehp(Maybe_Half_Phys(tmp), "\90H\93÷\8a\93\82É\8a\9a\82Ü\82ê\82Ä", KILLED_BY_AN);
         makeknown(BAG_OF_TRICKS);
+        abort_looting = TRUE;
         return 1;
     }
 
@@ -1832,7 +2021,7 @@ struct obj **cobjp;
 */
     You("%s%s\82ð\8aJ\82¯\82½\81D\81D\81D", (!cobj->cknown || !cobj->lknown) ? "\90T\8fd\82É" : "",
         the(xname(cobj)));
-    return use_container(cobjp, 0);
+    return use_container(cobjp, 0, (boolean) (cindex < ccount));
 }
 
 /* loot a container on the floor or loot saddle from mon. */
@@ -1851,14 +2040,16 @@ doloot()
     char qbuf[BUFSZ];
     int prev_inquiry = 0;
     boolean prev_loot = FALSE;
-    int num_conts;
+    int num_conts = 0;
+
+    abort_looting = FALSE;
 
     if (check_capacity((char *) 0)) {
         /* "Can't do that while carrying so much stuff." */
         return 0;
     }
     if (nohands(youmonst.data)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("have no hands!"); /* not `body_part(HAND)' */
 #else
         pline("\82 \82È\82½\82É\82Í\8eè\82ª\82È\82¢\81I");
@@ -1879,20 +2070,36 @@ doloot()
     cc.x = u.ux;
     cc.y = u.uy;
 
-lootcont:
+    if (iflags.menu_requested)
+        goto lootmon;
 
+ lootcont:
     if ((num_conts = container_at(cc.x, cc.y, TRUE)) > 0) {
         boolean anyfound = FALSE;
 
         if (!able_to_loot(cc.x, cc.y, TRUE))
             return 0;
 
+        if (Blind && !uarmg) {
+            /* if blind and without gloves, attempting to #loot at the
+               location of a cockatrice corpse is fatal before asking
+               whether to manipulate any containers */
+            for (nobj = sobj_at(CORPSE, cc.x, cc.y); nobj;
+                 nobj = nxtobj(nobj, CORPSE, TRUE))
+                if (will_feel_cockatrice(nobj, FALSE)) {
+                    feel_cockatrice(nobj, FALSE);
+                    /* if life-saved (or poly'd into stone golem),
+                       terminate attempt to loot */
+                    return 1;
+                }
+        }
+
         if (num_conts > 1) {
             /* use a menu to loot many containers */
             int n, i;
             winid win;
             anything any;
-            menu_item *pick_list = NULL;
+            menu_item *pick_list = (menu_item *) 0;
 
             any.a_void = 0;
             win = create_nhwindow(NHW_MENU);
@@ -1913,16 +2120,17 @@ lootcont:
             destroy_nhwindow(win);
 
             if (n > 0) {
-                for (i = 0; i < n; i++) {
-                    timepassed |= do_loot_cont(&pick_list[i].item.a_obj);
-                    if (multi < 0 || !pick_list[i].item.a_obj) {
+                for (i = 1; i <= n; i++) {
+                    cobj = pick_list[i - 1].item.a_obj;
+                    timepassed |= do_loot_cont(&cobj, i, n);
+                    if (abort_looting) {
+                        /* chest trap or magic bag explosion or <esc> */
                         free((genericptr_t) pick_list);
-                        return 1;
+                        return timepassed;
                     }
                 }
-            }
-            if (pick_list)
                 free((genericptr_t) pick_list);
+            }
             if (n != 0)
                 c = 'y';
         } else {
@@ -1930,7 +2138,7 @@ lootcont:
                 nobj = cobj->nexthere;
 
                 if (Is_container(cobj)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     c = ynq(safe_qbuf(qbuf, "There is ", " here, loot it?",
                                       cobj, doname, ansimpleoname,
                                       "a container"));
@@ -1945,11 +2153,10 @@ lootcont:
                         continue;
                     anyfound = TRUE;
 
-                    timepassed |= do_loot_cont(&cobj);
-                    /* might have triggered chest trap or magic bag explosion
-                     */
-                    if (multi < 0 || !cobj)
-                        return 1;
+                    timepassed |= do_loot_cont(&cobj, 1, 1);
+                    if (abort_looting)
+                        /* chest trap or magic bag explosion or <esc> */
+                        return timepassed;
                 }
             }
             if (anyfound)
@@ -1961,9 +2168,11 @@ lootcont:
 */
         You("\95æ\8dr\82ç\82µ\82ð\82·\82é\82É\82Í\8c@\82ç\82È\82­\82Ä\82Í\81D\81D\81D");
     }
+
     /*
      * 3.3.1 introduced directional looting for some things.
      */
+ lootmon:
     if (c != 'y' && mon_beside(u.ux, u.uy)) {
 /*JP
         if (!get_adjacent_loc("Loot in what direction?",
@@ -1981,7 +2190,7 @@ lootcont:
         } else
             underfoot = FALSE;
         if (u.dz < 0) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("%s to loot on the %s.", dont_find_anything,
                 ceiling(cc.x, cc.y));
 #else
@@ -2007,7 +2216,7 @@ lootcont:
         if (!underfoot) {
             if (container_at(cc.x, cc.y, FALSE)) {
                 if (mtmp) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     You_cant("loot anything %sthere with %s in the way.",
                              prev_inquiry ? "else " : "", mon_nam(mtmp));
 #else
@@ -2016,14 +2225,14 @@ lootcont:
 #endif
                     return timepassed;
                 } else {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     You("have to be at a container to loot it.");
 #else
                     You("\82Í\94 \82ð\8aJ\82¯\82é\82½\82ß\82É\82Í\93¯\82\88Ê\92u\82É\82¢\82È\82¯\82ê\82Î\82È\82ç\82È\82¢\81D");
 #endif
                 }
             } else {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 You("%s %sthere to loot.", dont_find_anything,
                     (prev_inquiry || prev_loot) ? "else " : "");
 #else
@@ -2034,7 +2243,7 @@ lootcont:
             }
         }
     } else if (c != 'y' && c != 'n') {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("%s %s to loot.", dont_find_anything,
             underfoot ? "here" : "there");
 #else
@@ -2088,8 +2297,7 @@ reverse_loot()
 */
             pline("\83I\81[\83P\81[\81C\82±\82±\82É\98d\98G\82ð\92u\82¢\82Ä\82¨\82±\82¤\81D");
     } else {
-        /* find original coffers chest if present, otherwise use nearest one
-         */
+        /* find original coffers chest if present, otherwise use nearest one */
         otmp = 0;
         for (coffers = fobj; coffers; coffers = coffers->nobj)
             if (coffers->otyp == CHEST) {
@@ -2116,8 +2324,8 @@ reverse_loot()
                 boxdummy = zeroobj, boxdummy.otyp = SPE_WIZARD_LOCK;
                 (void) boxlock(coffers, &boxdummy);
             }
-        } else if (levl[x][y].looted != T_LOOTED &&
-                   (mon = makemon(courtmon(), x, y, NO_MM_FLAGS)) != 0) {
+        } else if (levl[x][y].looted != T_LOOTED
+                   && (mon = makemon(courtmon(), x, y, NO_MM_FLAGS)) != 0) {
             freeinv(goldob);
             add_to_minv(mon, goldob);
 /*JP
@@ -2150,23 +2358,27 @@ boolean *prev_loot;
     struct obj *otmp;
     char qbuf[QBUFSZ];
 
-    /* 3.3.1 introduced the ability to remove saddle from a steed */
-    /*         *passed_info is set to TRUE if a loot query was given. */
-    /* *prev_loot is set to TRUE if something was actually acquired in here.
+    /* 3.3.1 introduced the ability to remove saddle from a steed.
+     *  *passed_info is set to TRUE if a loot query was given.
+     *  *prev_loot is set to TRUE if something was actually acquired in here.
      */
     if (mtmp && mtmp != u.usteed && (otmp = which_armor(mtmp, W_SADDLE))) {
         long unwornmask;
+
         if (passed_info)
             *passed_info = 1;
-        Sprintf(
-/*JP
-            qbuf, "Do you want to remove the saddle from %s?",
-*/
-            qbuf, "%s\82©\82ç\88Æ\82ð\82Í\82¸\82µ\82Ü\82·\82©\81H",
-            x_monnam(mtmp, ARTICLE_THE, (char *) 0, SUPPRESS_SADDLE, FALSE));
+#if 0 /*JP:T*/
+        Sprintf(qbuf, "Do you want to remove the saddle from %s?",
+                x_monnam(mtmp, ARTICLE_THE, (char *) 0,
+                         SUPPRESS_SADDLE, FALSE));
+#else
+        Sprintf(qbuf, "%s\82©\82ç\88Æ\82ð\82Í\82¸\82µ\82Ü\82·\82©\81H",
+                x_monnam(mtmp, ARTICLE_THE, (char *) 0,
+                         SUPPRESS_SADDLE, FALSE));
+#endif
         if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
             if (nolimbs(youmonst.data)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 You_cant("do that without limbs."); /* not body_part(HAND) */
 #else
                 You_cant("\8eè\82ª\82È\82¢\82Æ\82Å\82«\82È\82¢\81D");
@@ -2178,8 +2390,8 @@ boolean *prev_loot;
                 You("can't.  The saddle seems to be stuck to %s.",
 */
                 pline("\88Æ\82Í%s\82É\82­\82Á\82Â\82¢\82Ä\82¢\82é\82æ\82¤\82¾\81D",
-                    x_monnam(mtmp, ARTICLE_THE, (char *) 0, SUPPRESS_SADDLE,
-                             FALSE));
+                    x_monnam(mtmp, ARTICLE_THE, (char *) 0,
+                             SUPPRESS_SADDLE, FALSE));
                 /* the attempt costs you time */
                 return 1;
             }
@@ -2194,6 +2406,7 @@ boolean *prev_loot;
 */
             otmp = hold_another_object(otmp, "%s\82ð\97\8e\82Æ\82µ\82½\81I", doname(otmp),
                                        (const char *) 0);
+            nhUse(otmp);
             timepassed = rnd(3);
             if (prev_loot)
                 *prev_loot = TRUE;
@@ -2201,10 +2414,10 @@ boolean *prev_loot;
             return 0;
         }
     }
-    /* 3.4.0 introduced the ability to pick things up from within swallower's
-     * stomach */
+    /* 3.4.0 introduced ability to pick things up from swallower's stomach */
     if (u.uswallow) {
         int count = passed_info ? *passed_info : 0;
+
         timepassed = pickup(count);
     }
     return timepassed;
@@ -2238,6 +2451,28 @@ int depthin;
     return FALSE;
 }
 
+STATIC_OVL long
+boh_loss(container, held)
+struct obj *container;
+int held;
+{
+    /* sometimes toss objects if a cursed magic bag */
+    if (Is_mbag(container) && container->cursed && Has_contents(container)) {
+        long loss = 0L;
+        struct obj *curr, *otmp;
+
+        for (curr = container->cobj; curr; curr = otmp) {
+            otmp = curr->nobj;
+            if (!rn2(13)) {
+                obj_extract_self(curr);
+                loss += mbag_item_gone(held, curr);
+            }
+        }
+        return loss;
+    }
+    return 0;
+}
+
 /* Returns: -1 to stop, 1 item was inserted, 0 item was not inserted. */
 STATIC_PTR int
 in_container(obj)
@@ -2263,7 +2498,7 @@ register struct obj *obj;
         pline("\82»\82ê\82Í\8b»\96¡\82ð\82»\82»\82ç\82ê\82é\83g\83|\83\8d\83W\81[\82Ì\96â\91è\82¾\81D");
         return 0;
     } else if (obj->owornmask & (W_ARMOR | W_ACCESSORY)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Norep("You cannot %s %s you are wearing.",
               Icebox ? "refrigerate" : "stash", something);
 #else
@@ -2272,7 +2507,7 @@ register struct obj *obj;
 #endif
         return 0;
     } else if ((obj->otyp == LOADSTONE) && obj->cursed) {
-        obj->bknown = 1;
+        set_bknown(obj, 1);
 /*JP
         pline_The("stone%s won't leave your person.", plur(obj->quan));
 */
@@ -2303,16 +2538,16 @@ register struct obj *obj;
             return 0;
         }
         setuwep((struct obj *) 0);
+        /* This uwep check is obsolete.  It dates to 3.0 and earlier when
+         * unwielding Firebrand would be fatal in hell if hero had no other
+         * fire resistance.  Life-saving would force it to be re-wielded.
+         */
         if (uwep)
             return 0; /* unwielded, died, rewielded */
     } else if (obj == uswapwep) {
         setuswapwep((struct obj *) 0);
-        if (uswapwep)
-            return 0; /* unwielded, died, rewielded */
     } else if (obj == uquiver) {
         setuqwep((struct obj *) 0);
-        if (uquiver)
-            return 0; /* unwielded, died, rewielded */
     }
 
     if (fatal_corpse_mistake(obj, FALSE))
@@ -2341,14 +2576,14 @@ register struct obj *obj;
         (void) snuff_lit(obj);
 
     if (floor_container && costly_spot(u.ux, u.uy)) {
-        if (current_container->no_charge && !obj->unpaid) {
-            /* don't sell when putting the item into your own container */
-            obj->no_charge = 1;
-        } else if (obj->oclass != COIN_CLASS) {
-            /* sellobj() will take an unpaid item off the shop bill
-             * note: coins are handled later */
+        /* defer gold until after put-in message */
+        if (obj->oclass != COIN_CLASS) {
+            /* sellobj() will take an unpaid item off the shop bill */
             was_unpaid = obj->unpaid ? TRUE : FALSE;
-            sellobj_state(SELL_DELIBERATE);
+            /* don't sell when putting the item into your own container,
+             * but handle billing correctly */
+            sellobj_state(current_container->no_charge
+                          ? SELL_DONTSELL : SELL_DELIBERATE);
             sellobj(obj, u.ux, u.uy);
             sellobj_state(SELL_NORMAL);
         }
@@ -2358,6 +2593,7 @@ register struct obj *obj;
         /* stop any corpse timeouts when frozen */
         if (obj->otyp == CORPSE && obj->timed) {
             long rot_alarm = stop_timer(ROT_CORPSE, obj_to_any(obj));
+
             (void) stop_timer(REVIVE_MON, obj_to_any(obj));
             /* mark a non-reviving corpse as such */
             if (rot_alarm)
@@ -2374,6 +2610,19 @@ register struct obj *obj;
         if (was_unpaid)
             addtobill(obj, FALSE, FALSE, TRUE);
         obfree(obj, (struct obj *) 0);
+        /* if carried, shop goods will be flagged 'unpaid' and obfree() will
+           handle bill issues, but if on floor, we need to put them on bill
+           before deleting them (non-shop items will be flagged 'no_charge') */
+        if (floor_container
+            && costly_spot(current_container->ox, current_container->oy)) {
+            struct obj save_no_charge;
+
+            save_no_charge.no_charge = current_container->no_charge;
+            addtobill(current_container, FALSE, FALSE, FALSE);
+            /* addtobill() clears no charge; we need to set it back
+               so that useupf() doesn't double bill */
+            current_container->no_charge = save_no_charge.no_charge;
+        }
         delete_contents(current_container);
         if (!floor_container)
             useup(current_container);
@@ -2407,15 +2656,19 @@ register struct obj *obj;
      * update status immediately.
      */
     bot();
-
     return (current_container ? 1 : -1);
 }
 
+/* askchain() filter used by in_container();
+ * returns True if the container is intact and 'obj' isn't it, False if
+ * container is gone (magic bag explosion) or 'obj' is the container itself;
+ * also used by getobj() when picking a single item to stash
+ */
 int
 ck_bag(obj)
 struct obj *obj;
 {
-    return current_container && obj != current_container;
+    return (current_container && obj != current_container);
 }
 
 /* Returns: -1 to stop, 1 item was removed, 0 item was not removed. */
@@ -2452,12 +2705,8 @@ register struct obj *obj;
     obj_extract_self(obj);
     current_container->owt = weight(current_container);
 
-    if (Icebox && !age_is_relative(obj)) {
-        obj->age = monstermoves - obj->age; /* actual age */
-        if (obj->otyp == CORPSE)
-            start_corpse_timeout(obj);
-    }
-    /* simulated point of time */
+    if (Icebox)
+        removed_from_icebox(obj);
 
     if (!obj->unpaid && !carried(current_container)
         && costly_spot(current_container->ox, current_container->oy)) {
@@ -2470,16 +2719,16 @@ register struct obj *obj;
 
     otmp = addinv(obj);
     loadlev = near_capacity();
-    prinv(loadlev
+    prinv(loadlev ? ((loadlev < MOD_ENCUMBER)
 /*JP
-              ? (loadlev < MOD_ENCUMBER ? "You have a little trouble removing"
+                        ? "You have a little trouble removing"
 */
-              ? (loadlev < MOD_ENCUMBER ? "\82ð\89^\82Ô\82Ì\82Í\8f­\81X\8d¢\93ï\82¾\81D"
+                        ? "\82ð\89^\82Ô\82Ì\82Í\8f­\81X\8d¢\93ï\82¾\81D"
 /*JP
-                                        : "You have much trouble removing")
+                        : "You have much trouble removing")
 */
-                                        : "\82ð\89^\82Ô\82Ì\82Í\82©\82È\82è\8d¢\93ï\82¾\81D")
-              : (char *) 0,
+                        : "\82ð\89^\82Ô\82Ì\82Í\82©\82È\82è\8d¢\93ï\82¾\81D")
+                  : (char *) 0,
           otmp, count);
 
     if (is_gold) {
@@ -2488,6 +2737,18 @@ register struct obj *obj;
     return 1;
 }
 
+/* taking a corpse out of an ice box needs a couple of adjustments */
+STATIC_OVL void
+removed_from_icebox(obj)
+struct obj *obj;
+{
+    if (!age_is_relative(obj)) {
+        obj->age = monstermoves - obj->age; /* actual age */
+        if (obj->otyp == CORPSE)
+            start_corpse_timeout(obj);
+    }
+}
+
 /* an object inside a cursed bag of holding is being destroyed */
 STATIC_OVL long
 mbag_item_gone(held, item)
@@ -2503,7 +2764,7 @@ struct obj *item;
 */
         pline("%s\82Í\8fÁ\82¦\8b\8e\82Á\82½\81I", Doname2(item));
     else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("%s %s disappear!", Blind ? "notice" : "see", doname(item));
 #else
         You("%s\82ª\8c©\82¦\82È\82­\82È\82é\82Ì%s\81D", doname(item), Blind ? "\82É\8bC\82Ã\82¢\82½" : "\82ð\8c©\82½");
@@ -2518,59 +2779,84 @@ struct obj *item;
     return loss;
 }
 
-STATIC_OVL void
-observe_quantum_cat(box)
+/* used for #loot/apply, #tip, and final disclosure */
+void
+observe_quantum_cat(box, makecat, givemsg)
 struct obj *box;
+boolean makecat, givemsg;
 {
 /*JP
     static NEARDATA const char sc[] = "Schroedinger's Cat";
 */
     static NEARDATA const char sc[] = "\83V\83\85\83\8c\83f\83B\83\93\83K\81[\82Ì\94L";
     struct obj *deadcat;
-    struct monst *livecat;
+    struct monst *livecat = 0;
     xchar ox, oy;
+    boolean itsalive = !rn2(2);
 
-    box->spe = 0; /* box->owt will be updated below */
     if (get_obj_location(box, &ox, &oy, 0))
         box->ox = ox, box->oy = oy; /* in case it's being carried */
 
     /* this isn't really right, since any form of observation
        (telepathic or monster/object/food detection) ought to
-       force the determination of alive vs dead state; but basing
-       it just on opening the box is much simpler to cope with */
-    livecat = rn2(2)
-                  ? makemon(&mons[PM_HOUSECAT], box->ox, box->oy, NO_MINVENT)
-                  : 0;
-    if (livecat) {
-        livecat->mpeaceful = 1;
-        set_malign(livecat);
-        if (!canspotmon(livecat))
-/*JP
-            You("think %s brushed your %s.", something, body_part(FOOT));
-*/
-            You("%s\82ª\82 \82È\82½\82Ì%s\82ð\82­\82·\82®\82Á\82½\81D", something, body_part(FOOT));
-        else
-/*JP
-            pline("%s inside the box is still alive!", Monnam(livecat));
-*/
-            pline("\94 \82Ì\82È\82©\82Ì%s\82Í\82Ü\82¾\90\82«\82Ä\82¢\82é\81I", Monnam(livecat));
-        (void) christen_monst(livecat, sc);
+       force the determination of alive vs dead state; but basing it
+       just on opening or disclosing the box is much simpler to cope with */
+
+    /* SchroedingersBox already has a cat corpse in it */
+    deadcat = box->cobj;
+    if (itsalive) {
+        if (makecat)
+            livecat = makemon(&mons[PM_HOUSECAT], box->ox, box->oy,
+                              NO_MINVENT | MM_ADJACENTOK);
+        if (livecat) {
+            livecat->mpeaceful = 1;
+            set_malign(livecat);
+            if (givemsg) {
+                if (!canspotmon(livecat))
+#if 0 /*JP:T*/
+                    You("think %s brushed your %s.", something,
+                        body_part(FOOT));
+#else
+                    You("%s\82ª\82 \82È\82½\82Ì%s\82ð\82­\82·\82®\82Á\82½\82Æ\8ev\82Á\82½\81D", something,
+                        body_part(FOOT));
+#endif
+                else
+#if 0 /*JP:T*/
+                    pline("%s inside the box is still alive!",
+                          Monnam(livecat));
+#else
+                    pline("\94 \82Ì\82È\82©\82Ì%s\82Í\82Ü\82¾\90\82«\82Ä\82¢\82é\81I",
+                          Monnam(livecat));
+#endif
+            }
+            (void) christen_monst(livecat, sc);
+            if (deadcat) {
+                obj_extract_self(deadcat);
+                obfree(deadcat, (struct obj *) 0), deadcat = 0;
+            }
+            box->owt = weight(box);
+            box->spe = 0;
+        }
     } else {
-        deadcat =
-            mk_named_object(CORPSE, &mons[PM_HOUSECAT], box->ox, box->oy, sc);
+        box->spe = 0; /* now an ordinary box (with a cat corpse inside) */
         if (deadcat) {
-            obj_extract_self(deadcat);
-            (void) add_to_container(box, deadcat);
+            /* set_corpsenm() will start the rot timer that was removed
+               when makemon() created SchroedingersBox; start it from
+               now rather than from when this special corpse got created */
+            deadcat->age = monstermoves;
+            set_corpsenm(deadcat, PM_HOUSECAT);
+            deadcat = oname(deadcat, sc);
         }
-#if 0 /*JP*/
-        pline_The("%s inside the box is dead!",
-                  Hallucination ? rndmonnam(NULL) : "housecat");
+        if (givemsg)
+#if 0 /*JP:T*/
+            pline_The("%s inside the box is dead!",
+                      Hallucination ? rndmonnam((char *) 0) : "housecat");
 #else
-        pline_The("\94 \82Ì\92\86\82Ì%s\82Í\8e\80\82ñ\82Å\82¢\82é\81I",
-                  Hallucination ? rndmonnam(NULL) : "\94L");
+            pline_The("\94 \82Ì\92\86\82Ì%s\82Í\8e\80\82ñ\82Å\82¢\82é\81I",
+                      Hallucination ? rndmonnam((char *) 0) : "\94L");
 #endif
     }
-    box->owt = weight(box);
+    nhUse(deadcat);
     return;
 }
 
@@ -2587,23 +2873,36 @@ int FDECL((*fn), (OBJ_P));
 }
 
 STATIC_OVL void
-explain_container_prompt()
+explain_container_prompt(more_containers)
+boolean more_containers;
 {
     static const char *const explaintext[] = {
-#if 0 /*JP*/
-        "Container actions:", "", " : -- Look: examine contents",
-        " o -- Out: take things out", " i -- In: put things in",
+#if 0 /*JP:T*/
+        "Container actions:",
+        "",
+        " : -- Look: examine contents",
+        " o -- Out: take things out",
+        " i -- In: put things in",
         " b -- Both: first take things out, then put things in",
         " r -- Reversed: put things in, then take things out",
-        " s -- Stash: put one item in", " q -- Quit: do nothing",
-        " ? -- Help: display this text.", "", 0
+        " s -- Stash: put one item in", "",
+        " n -- Next: loot next selected container",
+        " q -- Quit: finished",
+        " ? -- Help: display this text.",
+        "", 0
 #else
-        "\93ü\82ê\95¨\82Ö\82Ì\8ds\93®\81F", "", " : -- Look: \92\86\90g\82ð\92²\82×\82é",
-        " o -- Out: \95¨\82ð\8fo\82·", " i -- In: \95¨\82ð\93ü\82ê\82é",
+        "\93ü\82ê\95¨\82Ö\82Ì\8ds\93®\81F",
+        "",
+        " : -- Look: \92\86\90g\82ð\92²\82×\82é",
+        " o -- Out: \95¨\82ð\8fo\82·",
+        " i -- In: \95¨\82ð\93ü\82ê\82é",
         " b -- Both: \82Ü\82¸\95¨\82ð\8fo\82µ\81A\82»\82ê\82©\82ç\95¨\82ð\93ü\82ê\82é",
         " r -- Reversed: \95¨\82ð\93ü\82ê\81A\82»\82ê\82©\82ç\95¨\82ð\8fo\82·",
-        " s -- Stash: \95¨\82ð\88ê\82Â\93ü\82ê\82é", " q -- Quit: \89½\82à\82µ\82È\82¢",
-        " ? -- Help: \82±\82ê\82ð\95\\8e¦\82·\82é", "", 0
+        " s -- Stash: \95¨\82ð\88ê\82Â\93ü\82ê\82é",
+        " n -- Next: \8e\9f\82É\91I\82ñ\82¾\93ü\82ê\95¨\82ð\92²\82×\82é",
+        " q -- Quit: \89½\82à\82µ\82È\82¢",
+        " ? -- Help: \82±\82ê\82ð\95\\8e¦\82·\82é",
+        "", 0
 #endif
     };
     const char *const *txtpp;
@@ -2611,8 +2910,11 @@ explain_container_prompt()
 
     /* "Do what with <container>? [:oibrsq or ?] (q)" */
     if ((win = create_nhwindow(NHW_TEXT)) != WIN_ERR) {
-        for (txtpp = explaintext; *txtpp; ++txtpp)
+        for (txtpp = explaintext; *txtpp; ++txtpp) {
+            if (!more_containers && !strncmp(*txtpp, " n ", 3))
+                continue;
             putstr(win, 0, *txtpp);
+        }
         display_nhwindow(win, FALSE);
         destroy_nhwindow(win);
     }
@@ -2622,7 +2924,7 @@ boolean
 u_handsy()
 {
     if (nohands(youmonst.data)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("have no hands!"); /* not `body_part(HAND)' */
 #else
         pline("\82 \82È\82½\82É\82Í\8eè\82ª\82È\82¢\81I");  /* not `body_part(HAND)' */
@@ -2641,21 +2943,29 @@ u_handsy()
 static const char stashable[] = { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, 0 };
 
 int
-use_container(objp, held)
+use_container(objp, held, more_containers)
 struct obj **objp;
 int held;
+boolean more_containers; /* True iff #loot multiple and this isn't last one */
 {
-    struct obj *curr, *otmp, *obj = *objp;
+    struct obj *otmp, *obj = *objp;
     boolean quantum_cat, cursed_mbag, loot_out, loot_in, loot_in_first,
         stash_one, inokay, outokay, outmaybe;
     char c, emptymsg[BUFSZ], qbuf[QBUFSZ], pbuf[QBUFSZ], xbuf[QBUFSZ];
     int used = 0;
+    long loss;
 
+    abort_looting = FALSE;
     emptymsg[0] = '\0';
 
     if (!u_handsy())
         return 0;
 
+    if (!obj->lknown) { /* do this in advance */
+        obj->lknown = 1;
+        if (held)
+            update_inventory();
+    }
     if (obj->olocked) {
 /*JP
         pline("%s locked.", Tobjnam(obj, "are"));
@@ -2666,7 +2976,6 @@ int held;
             You("must put it down to unlock.");
 */
             if (held) pline("\89º\82É\92u\82©\82È\82¢\82±\82Æ\82É\82Í\8c®\82ð\82Í\82¸\82¹\82È\82¢\81D");
-        obj->lknown = 1;
         return 0;
     } else if (obj->otrapped) {
         if (held)
@@ -2674,7 +2983,6 @@ int held;
             You("open %s...", the(xname(obj)));
 */
             You("%s\82ð\8aJ\82¯\82½\81D\81D\81D", the(xname(obj)));
-        obj->lknown = 1;
         (void) chest_trap(obj, HAND, FALSE);
         /* even if the trap fails, you've used up this turn */
         if (multi >= 0) { /* in case we didn't become paralyzed */
@@ -2685,38 +2993,32 @@ int held;
             multi_reason = "\94 \82ð\8aJ\82¯\82Ä\82¢\82é\8e\9e\82É";
             nomovemsg = "";
         }
+        abort_looting = TRUE;
         return 1;
     }
-    obj->lknown = 1;
 
     current_container = obj; /* for use by in/out_container */
-    /* from here on out, all early returns go through containerdone */
+    /*
+     * From here on out, all early returns go through 'containerdone:'.
+     */
 
     /* check for Schroedinger's Cat */
     quantum_cat = SchroedingersBox(current_container);
     if (quantum_cat) {
-        observe_quantum_cat(current_container);
+        observe_quantum_cat(current_container, TRUE, TRUE);
         used = 1;
     }
-    /* sometimes toss objects if a cursed magic bag */
-    cursed_mbag = (Is_mbag(current_container) && current_container->cursed
-                   && Has_contents(current_container));
-    if (cursed_mbag) {
-        long loss = 0L;
 
-        for (curr = current_container->cobj; curr; curr = otmp) {
-            otmp = curr->nobj;
-            if (!rn2(13)) {
-                obj_extract_self(curr);
-                loss += mbag_item_gone(held, curr);
-                used = 1;
-            }
-        }
-        if (loss)
+    cursed_mbag = Is_mbag(current_container)
+        && current_container->cursed
+        && Has_contents(current_container);
+    if (cursed_mbag
+        && (loss = boh_loss(current_container, held)) != 0) {
+        used = 1;
 /*JP
-            You("owe %ld %s for lost merchandise.", loss, currency(loss));
+        You("owe %ld %s for lost merchandise.", loss, currency(loss));
 */
-            You("\8e¸\82Á\82½\8f¤\95i\82Ì\82½\82ß\82É%ld%s\82Ì\95\89\8dÂ\82ð\95\89\82Á\82½\81D", loss, currency(loss));
+        You("\8e¸\82Á\82½\8f¤\95i\82Ì\82½\82ß\82É%ld%s\82Ì\95\89\8dÂ\82ð\95\89\82Á\82½\81D", loss, currency(loss));
         current_container->owt = weight(current_container);
     }
     inokay = (invent != 0
@@ -2744,20 +3046,23 @@ int held;
      * inventory is empty--taking out could alter that;
      * include do-both-reversed when 'i' is available,
      * even if container is empty--for similar reason;
-     * always include the quit choice ('q').
+     * include the next container choice ('n') when
+     * relevant, and make it the default;
+     * always include the quit choice ('q'), and make
+     * it the default if there's no next containter;
      * include the help choice (" or ?") if `cmdassist'
      * run-time option is set;
-     * (Player can pick any of (o,i,b,r,s,?) even when
+     * (Player can pick any of (o,i,b,r,n,s,?) even when
      * they're not listed among the available actions.)
      *
-     * Do what with <the/your/Shk's container>? [:oibrsq or ?] (q)
+     * Do what with <the/your/Shk's container>? [:oibrs nq or ?] (q)
      * or
-     * <The/Your/Shk's container> is empty.  Do what with it? [:irsq or ?]
+     * <The/Your/Shk's container> is empty.  Do what with it? [:irs nq or ?]
      */
-    for (;;) { /* repeats if '?' or ":' gets chosen */
+    for (;;) { /* repeats iff '?' or ':' gets chosen */
         outmaybe = (outokay || !current_container->cknown);
         if (!outmaybe)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             (void) safe_qbuf(qbuf, (char *) 0, " is empty.  Do what with it?",
                              current_container, Yname2, Ysimple_name2,
                              "This");
@@ -2767,7 +3072,7 @@ int held;
                              "\82±\82ê");
 #endif
         else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             (void) safe_qbuf(qbuf, "Do what with ", "?", current_container,
                              yname, ysimple_name, "it");
 #else
@@ -2775,34 +3080,43 @@ int held;
                              yname, ysimple_name, "\82±\82ê");
 #endif
         /* ask player about what to do with this container */
-        if (flags.menu_style == MENU_FULL) {
+        if (flags.menu_style == MENU_PARTIAL
+            || flags.menu_style == MENU_FULL) {
             if (!inokay && !outmaybe) {
                 /* nothing to take out, nothing to put in;
                    trying to do both will yield proper feedback */
                 c = 'b';
             } else {
                 c = in_or_out_menu(qbuf, current_container, outmaybe, inokay,
-                                   (used != 0));
+                                   (boolean) (used != 0), more_containers);
             }
-        } else {               /* TRADITIONAL, COMBINATION, or PARTIAL */
-            xbuf[0] = '\0';    /* list of extra acceptable responses */
-            Strcpy(pbuf, ":"); /* look inside */
+        } else { /* TRADITIONAL or COMBINATION */
+            xbuf[0] = '\0'; /* list of extra acceptable responses */
+            Strcpy(pbuf, ":");                   /* look inside */
             Strcat(outmaybe ? pbuf : xbuf, "o"); /* take out */
             Strcat(inokay ? pbuf : xbuf, "i");   /* put in */
             Strcat(outmaybe ? pbuf : xbuf, "b"); /* both */
             Strcat(inokay ? pbuf : xbuf, "rs");  /* reversed, stash */
+            Strcat(pbuf, " ");                   /* separator */
+            Strcat(more_containers ? pbuf : xbuf, "n"); /* next container */
             Strcat(pbuf, "q");                   /* quit */
             if (iflags.cmdassist)
+                /* this unintentionally allows user to answer with 'o' or
+                   'r'; fortunately, those are already valid choices here */
+#if 0 /*JP:T*/
                 Strcat(pbuf, " or ?"); /* help */
+#else
+                Strcat(pbuf, "\82Ü\82½\82Í?"); /* help */
+#endif
             else
                 Strcat(xbuf, "?");
             if (*xbuf)
                 Strcat(strcat(pbuf, "\033"), xbuf);
-            c = yn_function(qbuf, pbuf, 'q');
-        } /* FULL vs other modes */
+            c = yn_function(qbuf, pbuf, more_containers ? 'n' : 'q');
+        } /* PARTIAL|FULL vs other modes */
 
         if (c == '?') {
-            explain_container_prompt();
+            explain_container_prompt(more_containers);
         } else if (c == ':') { /* note: will set obj->cknown */
             if (!current_container->cknown)
                 used = 1; /* gaining info */
@@ -2811,7 +3125,9 @@ int held;
             break;
     } /* loop until something other than '?' or ':' is picked */
 
-    if (c == 'q') /* [not strictly needed; falling through works] */
+    if (c == 'q')
+        abort_looting = TRUE;
+    if (c == 'n' || c == 'q') /* [not strictly needed; falling thru works] */
         goto containerdone;
     loot_out = (c == 'o' || c == 'b' || c == 'r');
     loot_in = (c == 'i' || c == 'b' || c == 'r');
@@ -2831,12 +3147,13 @@ int held;
                 used |= traditional_loot(FALSE);
             else
                 used |= (menu_loot(0, FALSE) > 0);
+            add_valid_menu_class(0);
         }
     }
 
     if ((loot_in || stash_one)
         && (!invent || (invent == current_container && !invent->nobj))) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("don't have anything%s to %s.", invent ? " else" : "",
             stash_one ? "stash" : "put in");
 #else
@@ -2855,6 +3172,7 @@ int held;
             used |= traditional_loot(TRUE);
         else
             used |= (menu_loot(0, TRUE) > 0);
+        add_valid_menu_class(0);
     } else if (stash_one) {
         /* put one item into container */
         if ((otmp = getobj(stashable, "stash")) != 0) {
@@ -2863,11 +3181,7 @@ int held;
             } else {
                 /* couldn't put selected item into container for some
                    reason; might need to undo splitobj() */
-                for (curr = invent; curr; curr = curr->nobj)
-                    if (curr->nobj == otmp)
-                        break;
-                if (curr && curr->invlet == otmp->invlet)
-                    (void) merged(&curr, &otmp);
+                (void) unsplitobj(otmp);
             }
         }
     }
@@ -2888,10 +3202,11 @@ int held;
                 used |= traditional_loot(FALSE);
             else
                 used |= (menu_loot(0, FALSE) > 0);
+            add_valid_menu_class(0);
         }
     }
 
-containerdone:
+ containerdone:
     if (used) {
         /* Not completely correct; if we put something in without knowing
            whatever was already inside, now we suddenly do.  That can't
@@ -2903,7 +3218,10 @@ containerdone:
     }
 
     *objp = current_container; /* might have become null */
-    current_container = 0;     /* avoid hanging on to stale pointer */
+    if (current_container)
+        current_container = 0; /* avoid hanging on to stale pointer */
+    else
+        abort_looting = TRUE;
     return used;
 }
 
@@ -2914,7 +3232,7 @@ boolean put_in;
 {
     int FDECL((*actionfunc), (OBJ_P)), FDECL((*checkfunc), (OBJ_P));
     struct obj **objlist;
-    char selection[MAXOCLASSES + 1];
+    char selection[MAXOCLASSES + 10]; /* +10: room for B,U,C,X plus slop */
     const char *action;
     boolean one_by_one, allflag;
     int used = 0, menu_on_request = 0;
@@ -2974,9 +3292,7 @@ boolean put_in;
         Sprintf(buf, "%s what type of objects?", action);
 */
         Sprintf(buf, "\82Ç\82Ì\8eí\97Þ\82Ì\82à\82Ì\82ð%s\81H", action);
-        mflags = put_in
-                     ? ALL_TYPES | BUC_ALLBKNOWN | BUC_UNKNOWN
-                     : ALL_TYPES | CHOOSE_ALL | BUC_ALLBKNOWN | BUC_UNKNOWN;
+        mflags = (ALL_TYPES | UNPAID_TYPES | BUCX_TYPES | CHOOSE_ALL);
         n = query_category(buf, put_in ? invent : current_container->cobj,
                            mflags, &pick_list, PICK_ANY);
         if (!n)
@@ -2993,12 +3309,23 @@ boolean put_in;
     }
 
     if (loot_everything) {
-        current_container->cknown = 1;
-        for (otmp = current_container->cobj; otmp; otmp = otmp2) {
-            otmp2 = otmp->nobj;
-            res = out_container(otmp);
-            if (res < 0)
-                break;
+        if (!put_in) {
+            current_container->cknown = 1;
+            for (otmp = current_container->cobj; otmp; otmp = otmp2) {
+                otmp2 = otmp->nobj;
+                res = out_container(otmp);
+                if (res < 0)
+                    break;
+                n_looted += res;
+            }
+        } else {
+            for (otmp = invent; otmp && current_container; otmp = otmp2) {
+                otmp2 = otmp->nobj;
+                res = in_container(otmp);
+                if (res < 0)
+                    break;
+                n_looted += res;
+            }
         }
     } else {
         mflags = INVORDER_SORT;
@@ -3010,7 +3337,7 @@ boolean put_in;
         Sprintf(buf, "%s what?", action);
 */
         Sprintf(buf, "\89½\82ð%s\81H", action);
-        n = query_objlist(buf, put_in ? invent : current_container->cobj,
+        n = query_objlist(buf, put_in ? &invent : &(current_container->cobj),
                           mflags, &pick_list, PICK_ANY,
                           all_categories ? allow_all : allow_category);
         if (n) {
@@ -3042,13 +3369,13 @@ boolean put_in;
 }
 
 STATIC_OVL char
-in_or_out_menu(prompt, obj, outokay, inokay, alreadyused)
+in_or_out_menu(prompt, obj, outokay, inokay, alreadyused, more_containers)
 const char *prompt;
 struct obj *obj;
-boolean outokay, inokay, alreadyused;
+boolean outokay, inokay, alreadyused, more_containers;
 {
     /* underscore is not a choice; it's used to skip element [0] */
-    static const char lootchars[] = "_:oibrsq", abc_chars[] = "_:abcdeq";
+    static const char lootchars[] = "_:oibrsnq", abc_chars[] = "_:abcdenq";
     winid win;
     anything any;
     menu_item *pick_list;
@@ -3096,7 +3423,7 @@ boolean outokay, inokay, alreadyused;
     }
     if (inokay) {
         any.a_int = 5; /* 'r' */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Sprintf(buf, "%sput in, then take out",
                 outokay ? "both reversed; " : "");
 #else
@@ -3113,23 +3440,38 @@ boolean outokay, inokay, alreadyused;
         add_menu(win, NO_GLYPH, &any, menuselector[any.a_int], 0, ATR_NONE,
                  buf, MENU_UNSELECTED);
     }
-    any.a_int = 7; /* 'q' */
+    any.a_int = 0;
+    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
+    if (more_containers) {
+        any.a_int = 7; /* 'n' */
+#if 0 /*JP:T*/
+        add_menu(win, NO_GLYPH, &any, menuselector[any.a_int], 0, ATR_NONE,
+                 "loot next container", MENU_SELECTED);
+#else
+        add_menu(win, NO_GLYPH, &any, menuselector[any.a_int], 0, ATR_NONE,
+                 "\8e\9f\82Ì\94 \82ð\8aJ\82¯\82é", MENU_SELECTED);
+#endif
+    }
+    any.a_int = 8; /* 'q' */
 /*JP
     Strcpy(buf, alreadyused ? "done" : "do nothing");
 */
     Strcpy(buf, alreadyused ? "\8fI\82í\82é" : "\89½\82à\82µ\82È\82¢");
     add_menu(win, NO_GLYPH, &any, menuselector[any.a_int], 0, ATR_NONE, buf,
-             MENU_SELECTED);
+             more_containers ? MENU_UNSELECTED : MENU_SELECTED);
 
     end_menu(win, prompt);
     n = select_menu(win, PICK_ONE, &pick_list);
     destroy_nhwindow(win);
     if (n > 0) {
-        n = pick_list[0].item.a_int;
+        int k = pick_list[0].item.a_int;
+
+        if (n > 1 && k == (more_containers ? 7 : 8))
+            k = pick_list[1].item.a_int;
         free((genericptr_t) pick_list);
-        return lootchars[n]; /* :,o,i,b,r,s,q */
+        return lootchars[k]; /* :,o,i,b,r,s,n,q */
     }
-    return 'q'; /* quit */
+    return (n == 0 && more_containers) ? 'n' : 'q'; /* next or quit */
 }
 
 static const char tippables[] = { ALL_CLASSES, TOOL_CLASS, 0 };
@@ -3154,7 +3496,7 @@ dotip()
 
     /* check floor container(s) first; at most one will be accessed */
     if ((boxes = container_at(cc.x, cc.y, TRUE)) > 0) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Sprintf(buf, "You can't tip %s while carrying so much.",
                 !flags.verbose ? "a container" : (boxes > 1) ? "one" : "it");
 #else
@@ -3167,7 +3509,7 @@ dotip()
                 int n, i;
                 winid win;
                 anything any;
-                menu_item *pick_list = NULL;
+                menu_item *pick_list = (menu_item *) 0;
                 struct obj dummyobj, *otmp;
 
                 any = zeroany;
@@ -3190,10 +3532,18 @@ dotip()
                     /* use 'i' for inventory unless there are so many
                        containers that it's already being used */
                     i = (i <= 'i' - 'a' && !flags.lootabc) ? 'i' : 0;
+#if 0 /*JP:T*/
                     add_menu(win, NO_GLYPH, &any, i, 0, ATR_NONE,
                              "tip something being carried", MENU_SELECTED);
+#else
+                    add_menu(win, NO_GLYPH, &any, i, 0, ATR_NONE,
+                             "\93ü\82ê\95¨\82ð\82Ð\82Á\82­\82è\82©\82¦\82·", MENU_SELECTED);
+#endif
                 }
+/*JP
                 end_menu(win, "Tip which container?");
+*/
+                end_menu(win, "\82Ç\82Ì\93ü\82ê\95¨\82ð\82Ð\82Á\82­\82è\82©\82¦\82·\81H");
                 n = select_menu(win, PICK_ONE, &pick_list);
                 destroy_nhwindow(win);
                 /*
@@ -3220,7 +3570,7 @@ dotip()
                     nobj = cobj->nexthere;
                     if (!Is_container(cobj))
                         continue;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     c = ynq(safe_qbuf(qbuf, "There is ", " here, tip it?",
                                       cobj,
                                       doname, ansimpleoname, "container"));
@@ -3292,13 +3642,13 @@ dotip()
 */
             Strcpy(buf, "\82»\82µ\82Ä\8f\99\81X\82É\8eU\82Á\82Ä\82¢\82Á\82½\81D");
         else if (is_lava(u.ux, u.uy))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             Sprintf(buf, " and immediately %s away",
                     vtense(spillage, "burn"));
 #else
             Strcpy(buf, "\82»\82µ\82Ä\82·\82®\82É\94R\82¦\82Â\82«\82½\81D");
 #endif
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         pline("Some %s %s onto the %s%s.", spillage,
               vtense(spillage, "spill"), surface(u.ux, u.uy), buf);
 #else
@@ -3333,25 +3683,36 @@ tipcontainer(box)
 struct obj *box; /* or bag */
 {
     xchar ox = u.ux, oy = u.uy; /* #tip only works at hero's location */
-    boolean empty_it = FALSE,
-            /* Shop handling:  can't rely on the container's own unpaid
-               or no_charge status because contents might differ with it.
-               A carried container's contents will be flagged as unpaid
-               or not, as appropriate, and need no special handling here.
-               Items owned by the hero get sold to the shop without
-               confirmation as with other uncontrolled drops.  A floor
-               container's contents will be marked no_charge if owned by
-               hero, otherwise they're owned by the shop.  By passing
-               the contents through shop billing, they end up getting
-               treated the same as in the carried case.   We do so one
-               item at a time instead of doing whole container at once
-               to reduce the chance of exhausting shk's billing capacity. */
-        maybeshopgoods = !carried(box) && costly_spot(ox, oy);
+    boolean empty_it = FALSE, maybeshopgoods;
+
+    /* box is either held or on floor at hero's spot; no need to check for
+       nesting; when held, we need to update its location to match hero's;
+       for floor, the coordinate updating is redundant */
+    if (get_obj_location(box, &ox, &oy, 0))
+        box->ox = ox, box->oy = oy;
+
+    /* Shop handling:  can't rely on the container's own unpaid
+       or no_charge status because contents might differ with it.
+       A carried container's contents will be flagged as unpaid
+       or not, as appropriate, and need no special handling here.
+       Items owned by the hero get sold to the shop without
+       confirmation as with other uncontrolled drops.  A floor
+       container's contents will be marked no_charge if owned by
+       hero, otherwise they're owned by the shop.  By passing
+       the contents through shop billing, they end up getting
+       treated the same as in the carried case.   We do so one
+       item at a time instead of doing whole container at once
+       to reduce the chance of exhausting shk's billing capacity. */
+    maybeshopgoods = !carried(box) && costly_spot(box->ox, box->oy);
 
     /* caveat: this assumes that cknown, lknown, olocked, and otrapped
        fields haven't been overloaded to mean something special for the
        non-standard "container" horn of plenty */
-    box->lknown = 1;
+    if (!box->lknown) {
+        box->lknown = 1;
+        if (carried(box))
+            update_inventory(); /* jumping the gun slightly; hope that's ok */
+    }
     if (box->olocked) {
 /*JP
         pline("It's locked.");
@@ -3385,7 +3746,7 @@ struct obj *box; /* or bag */
 
         if (box->spe < old_spe) {
             if (bag)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline((seen == 0) ? "Nothing seems to happen."
                                   : (seen == 1) ? "A monster appears."
                                                 : "Monsters appear!");
@@ -3405,14 +3766,14 @@ struct obj *box; /* or bag */
     } else if (SchroedingersBox(box)) {
         char yourbuf[BUFSZ];
 
-        observe_quantum_cat(box);
+        observe_quantum_cat(box, TRUE, TRUE);
         if (!Has_contents(box)) /* evidently a live cat came out */
             /* container type of "large box" is inferred */
 /*JP
             pline("%sbox is now empty.", Shk_Your(yourbuf, box));
 */
             pline("%s\94 \82Í\8bó\82É\82È\82Á\82½\81D", Shk_Your(yourbuf, box));
-        else /* holds cat corpse or other random stuff */
+        else /* holds cat corpse */
             empty_it = TRUE;
         box->cknown = 1;
     } else if (!Has_contents(box)) {
@@ -3427,7 +3788,7 @@ struct obj *box; /* or bag */
 
     if (empty_it) {
         struct obj *otmp, *nobj;
-        boolean verbose = FALSE, highdrop = !can_reach_floor(TRUE),
+        boolean terse, highdrop = !can_reach_floor(TRUE),
                 altarizing = IS_ALTAR(levl[ox][oy].typ),
                 cursed_mbag = (Is_mbag(box) && box->cursed);
         int held = carried(box);
@@ -3435,11 +3796,17 @@ struct obj *box; /* or bag */
 
         if (u.uswallow)
             highdrop = altarizing = FALSE;
+        terse = !(highdrop || altarizing || costly_spot(box->ox, box->oy));
         box->cknown = 1;
-#if 0 /*JP*/
+        /* Terse formatting is
+         * "Objects spill out: obj1, obj2, obj3, ..., objN."
+         * If any other messages intervene between objects, we revert to
+         * "ObjK drops to the floor.", "ObjL drops to the floor.", &c.
+         */
+#if 0 /*JP:T*/
         pline("%s out%c",
               box->cobj->nobj ? "Objects spill" : "An object spills",
-              !(highdrop || altarizing) ? ':' : '.');
+              terse ? ':' : '.');
 #else
         pline("\92\86\90g\82ª\8fo\82Ä\82«\82½%s",
               !(highdrop || altarizing) ? "\81F" : "\81D");
@@ -3447,10 +3814,14 @@ struct obj *box; /* or bag */
         for (otmp = box->cobj; otmp; otmp = nobj) {
             nobj = otmp->nobj;
             obj_extract_self(otmp);
-            if (cursed_mbag && !rn2(13)) {
+            otmp->ox = box->ox, otmp->oy = box->oy;
+
+            if (box->otyp == ICE_BOX) {
+                removed_from_icebox(otmp); /* resume rotting for corpse */
+            } else if (cursed_mbag && !rn2(13)) {
                 loss += mbag_item_gone(held, otmp);
                 /* abbreviated drop format is no longer appropriate */
-                verbose = TRUE;
+                terse = FALSE;
                 continue;
             }
 
@@ -3461,24 +3832,28 @@ struct obj *box; /* or bag */
 
             if (highdrop) {
                 /* might break or fall down stairs; handles altars itself */
-                hitfloor(otmp);
+                hitfloor(otmp, TRUE);
             } else {
-                if (altarizing)
+                if (altarizing) {
                     doaltarobj(otmp);
-                else if (verbose)
-#if 0 /*JP*/
+                } else if (!terse) {
+#if 0 /*JP:T*/
                     pline("%s %s to the %s.", Doname2(otmp),
                           otense(otmp, "drop"), surface(ox, oy));
 #else
                     pline("%s\82Í%s\82Ì\8fã\82É\97\8e\82¿\82½\81D", Doname2(otmp),
                           surface(ox, oy));
 #endif
-                else
+                } else {
 /*JP
                     pline("%s%c", doname(otmp), nobj ? ',' : '.');
 */
                     pline("%s%s", doname(otmp), nobj ? "\81C" : "\81D");
+                    iflags.last_msg = PLNMSG_OBJNAM_ONLY;
+                }
                 dropy(otmp);
+                if (iflags.last_msg != PLNMSG_OBJNAM_ONLY)
+                    terse = FALSE; /* terse formatting has been interrupted */
             }
             if (maybeshopgoods)
                 iflags.suppress_price--; /* reset */
@@ -3492,6 +3867,8 @@ struct obj *box; /* or bag */
         if (held)
             (void) encumber_msg();
     }
+    if (carried(box)) /* box is now empty with cknown set */
+        update_inventory();
 }
 
 /*pickup.c*/