]> granicus.if.org Git - nethack/commitdiff
Allow dropping just picked up items
authorPasi Kallinen <paxed@alt.org>
Fri, 17 Sep 2021 17:52:54 +0000 (20:52 +0300)
committerPasi Kallinen <paxed@alt.org>
Fri, 17 Sep 2021 18:00:06 +0000 (21:00 +0300)
When using a menu to drop or put in items into a container,
allow putting in the item (or items) you picked up previously,
by selecting the 'P' entry from the item class menu

Inspired by the itemcat patch by Stanislav Traykov.

Invalidates saves and bones.

14 files changed:
doc/Guidebook.mn
doc/Guidebook.tex
doc/fixes37.0
include/decl.h
include/extern.h
include/hack.h
include/obj.h
include/patchlevel.h
src/allmain.c
src/decl.c
src/do.c
src/invent.c
src/mkobj.c
src/pickup.c

index baecd5e3f6c78b7ba14b652c7e3caa4fa8b61079..b26f0df2bd39faa80fcc3f6481b0a3ba9e071912 100644 (file)
@@ -729,6 +729,8 @@ drop all objects known to be uncursed.
 drop all objects known to be cursed.
 .PL DX
 drop all objects of unknown B/U/C status.
+.PL DP
+drop objects picked up last.
 .PL Da
 drop all objects, without asking for confirmation.
 .PL Di
@@ -826,6 +828,8 @@ list all items known to be uncursed;
 list all items known to be cursed;
 .PL IX
 list all items whose bless/curse status is unknown;
+.PL IP
+list items picked up last;
 .PL I$
 count your money.
 .PE
index 0818c88124cd2be4c5327706f3857ff9d4fd252b..27c637bca0931fd6a2288993fff6e6a43d817638 100644 (file)
@@ -829,6 +829,7 @@ the bless\-ed/\-un\-curs\-ed/\-curs\-ed groups may be typed.\\
 {\tt DU}  --- drop all objects known to be uncursed.\\
 {\tt DC}  --- drop all objects known to be cursed.\\
 {\tt DX}  --- drop all objects of unknown B/U/C status.\\
+{\tt DP}  --- drop objects picked up last.\\
 {\tt Da}  --- drop all objects, without asking for confirmation.\\
 {\tt Di}  --- examine your inventory before dropping anything.\\
 {\tt Du}  --- drop only unpaid objects (when in a shop).\\
@@ -903,6 +904,7 @@ for potions.\\
 {\tt IU} --- list all items known to be uncursed;\\
 {\tt IC} --- list all items known to be cursed;\\
 {\tt IX} --- list all items whose bless/curse status is unknown;\\
+{\tt IP} --- list items picked up last;\\
 {\tt I\$} --- count your money.
 %.ei
 %.ed
index 6c84ddbfca9569a31caaf3db1cc0de51b0b5b45f..a4f773b2773e158961a382b39b407556799735a0 100644 (file)
@@ -1158,6 +1158,7 @@ new bigroom variant, a boulder maze
 vomiting on an altar provokes the deities wrath
 branch stairs have a different glyph, show up in yellow color in tty
 duration of confusion when drinking booze depends upon hunger state
+using 'D' allows dropping items picked up previously
 
 
 Platform- and/or Interface-Specific New Features
index 62105755112a592c98e332865a5f6ab14dfb16aa..88eb54e3b2623770750391adc4e2c96a85af9dfa 100644 (file)
@@ -1094,6 +1094,8 @@ struct instance_globals {
     boolean class_filter;
     boolean bucx_filter;
     boolean shop_filter;
+    boolean picked_filter;
+    boolean loot_reset_justpicked;
 
     /* pline.c */
     unsigned pline_flags;
index 5ea0b0eb6fa7f36de6b6484b62417a55ade533f0..5e328c24b64af17104e37dbb765ad921535b464b 100644 (file)
@@ -1076,7 +1076,7 @@ extern void free_pickinv_cache(void);
 extern int count_unpaid(struct obj *);
 extern int count_buc(struct obj *, int, boolean(*)(struct obj *));
 extern void tally_BUCX(struct obj *, boolean, int *, int *, int *, int *,
-                       int *);
+                       int *, int *);
 extern long count_contents(struct obj *, boolean, boolean, boolean, boolean);
 extern void carry_obj_effects(struct obj *);
 extern const char *currency(long);
@@ -1950,6 +1950,9 @@ extern boolean allow_category(struct obj *);
 extern boolean is_worn_by_type(struct obj *);
 extern int ck_bag(struct obj *);
 extern void removed_from_icebox(struct obj *);
+extern void reset_justpicked(struct obj *);
+extern int count_justpicked(struct obj *);
+extern struct obj *find_justpicked(struct obj *);
 extern int pickup(int);
 extern int pickup_object(struct obj *, long, boolean);
 extern int query_category(const char *, struct obj *, int, menu_item **, int);
index aed0711652f32728dbfb0147fda1599ace5e2425..2e87e21e143118fffab1405d5132d18e078422bb 100644 (file)
@@ -366,6 +366,7 @@ typedef struct sortloot_item Loot;
 #define BUC_CURSED        0x0200
 #define BUC_UNCURSED      0x0400
 #define BUC_UNKNOWN       0x0800
+#define JUSTPICKED        0x1000
 #define BUC_ALLBKNOWN (BUC_BLESSED | BUC_CURSED | BUC_UNCURSED)
 #define BUCX_TYPES (BUC_ALLBKNOWN | BUC_UNKNOWN)
 #define ALL_TYPES_SELECTED -2
index 107931f7405ca9158465bf77ac00648ac50e8889..cb99cfc2ccee123845d4843901b9f096f6ad89c1 100644 (file)
@@ -117,7 +117,8 @@ struct obj {
     Bitfield(cknown, 1); /* for containers (including statues): the contents
                           * are known; also applicable to tins */
     Bitfield(lknown, 1); /* locked/unlocked status is known */
-    /* 4 free bits */
+    Bitfield(pickup_prev, 1); /* was picked up previously */
+    /* 3 free bits */
 
     int corpsenm;         /* type of corpse is mons[corpsenm] */
 #define leashmon corpsenm /* gets m_id of attached pet */
index d00a857d8d08c64cad16ee1f22220663d1879c09..862453b827f59fd56b86dba2834b82d5de60046c 100644 (file)
@@ -17,7 +17,7 @@
  * Incrementing EDITLEVEL can be used to force invalidation of old bones
  * and save files.
  */
-#define EDITLEVEL 40
+#define EDITLEVEL 41
 
 /*
  * Development status possibilities.
index 3b10ecc79bb1ded99ed6d0bcf1601d02cc00abb2..6270fada644c96ccecfee0f4f532b86469abd4d3 100644 (file)
@@ -57,6 +57,7 @@ moveloop_preamble(boolean resuming)
     if (!resuming) { /* new game */
         g.context.rndencode = rnd(9000);
         set_wear((struct obj *) 0); /* for side-effects of starting gear */
+        reset_justpicked(g.invent);
         (void) pickup(1);      /* autopickup at initial location */
         /* only matters if someday a character is able to start with
            clairvoyance (wizard with cornuthaum perhaps?); without this,
index e9de48cd1263ae5907306f9aa08cde339f3cac91..a2ef2dca591e9e55de5982c5426cca435a50f821 100644 (file)
@@ -553,6 +553,8 @@ const struct instance_globals g_init = {
     UNDEFINED_VALUE, /* class_filter */
     UNDEFINED_VALUE, /* bucx_filter */
     UNDEFINED_VALUE, /* shop_filter */
+    UNDEFINED_VALUE, /* picked_filter */
+    UNDEFINED_VALUE, /* loot_reset_justpicked */
 
     /* pline.c */
     0, /* pline_flags */
index aeba4411863c8a7a75122e9feb75ad37ad86d503..0747fe098aaf92dae9f71f4ddd3ba3840f225a41 100644 (file)
--- a/src/do.c
+++ b/src/do.c
@@ -12,6 +12,7 @@ static void polymorph_sink(void);
 static boolean teleport_sink(void);
 static void dosinkring(struct obj *);
 static int drop(struct obj *);
+static int menudrop_split(struct obj *, int);
 static boolean engulfer_digests_food(struct obj *);
 static int wipeoff(void);
 static int menu_drop(int);
@@ -825,15 +826,32 @@ doddrop(void)
     return result;
 }
 
+static int
+menudrop_split(struct obj *otmp, int cnt)
+{
+    if (cnt && cnt < otmp->quan) {
+        if (welded(otmp)) {
+            ; /* don't split */
+        } else if (otmp->otyp == LOADSTONE && otmp->cursed) {
+            /* same kludge as getobj(), for canletgo()'s use */
+            otmp->corpsenm = (int) cnt; /* don't split */
+        } else {
+            otmp = splitobj(otmp, cnt);
+        }
+    }
+    return drop(otmp);
+}
+
 /* Drop things from the hero's inventory, using a menu. */
 static int
 menu_drop(int retry)
 {
     int n, i, n_dropped = 0;
-    long cnt;
     struct obj *otmp, *otmp2;
     menu_item *pick_list;
     boolean all_categories = TRUE, drop_everything = FALSE, autopick = FALSE;
+    boolean drop_justpicked = FALSE;
+    long justpicked_quan = 0;
 
     if (retry) {
         all_categories = (retry == -2);
@@ -842,7 +860,7 @@ menu_drop(int retry)
         n = query_category("Drop what type of items?", g.invent,
                            (UNPAID_TYPES | ALL_TYPES | CHOOSE_ALL
                             | BUC_BLESSED | BUC_CURSED | BUC_UNCURSED
-                            | BUC_UNKNOWN | INCLUDE_VENOM),
+                            | BUC_UNKNOWN | JUSTPICKED | INCLUDE_VENOM),
                            &pick_list, PICK_ANY);
         if (!n)
             goto drop_done;
@@ -851,6 +869,10 @@ menu_drop(int retry)
                 all_categories = TRUE;
             } else if (pick_list[i].item.a_int == 'A') {
                 drop_everything = autopick = TRUE;
+            } else if (pick_list[i].item.a_int == 'P') {
+                justpicked_quan = max(0, pick_list[i].count);
+                drop_justpicked = TRUE;
+                add_valid_menu_class(pick_list[i].item.a_int);
             } else {
                 add_valid_menu_class(pick_list[i].item.a_int);
                 drop_everything = FALSE;
@@ -898,6 +920,11 @@ menu_drop(int retry)
         /* we might not have dropped everything (worn armor, welded weapon,
            cursed loadstones), so reset any remaining inventory to normal */
         bypass_objlist(g.invent, FALSE);
+    } else if (drop_justpicked && count_justpicked(g.invent) == 1) {
+        /* drop the just picked item automatically, if only one stack */
+        otmp = find_justpicked(g.invent);
+        if (otmp)
+            n_dropped += menudrop_split(otmp, justpicked_quan);
     } else {
         /* should coordinate with perm invent, maybe not show worn items */
         n = query_objlist("What would you like to drop?", &g.invent,
@@ -926,18 +953,7 @@ menu_drop(int retry)
                 if (!otmp2 || !otmp2->bypass)
                     continue;
                 /* found next selected invent item */
-                cnt = pick_list[i].count;
-                if (cnt < otmp->quan) {
-                    if (welded(otmp)) {
-                        ; /* don't split */
-                    } else if (otmp->otyp == LOADSTONE && otmp->cursed) {
-                        /* same kludge as getobj(), for canletgo()'s use */
-                        otmp->corpsenm = (int) cnt; /* don't split */
-                    } else {
-                        otmp = splitobj(otmp, cnt);
-                    }
-                }
-                n_dropped += drop(otmp);
+                n_dropped += menudrop_split(otmp, pick_list[i].count);
             }
             bypass_objlist(g.invent, FALSE); /* reset g.invent to normal */
             free((genericptr_t) pick_list);
index afd5efe09137c5364f0222127c382e0486f19f36..93e552870376dd99b252cd5a51a52418a4ad3c2a 100644 (file)
@@ -719,6 +719,9 @@ merged(struct obj **potmp, struct obj **pobj)
             otmp = *potmp = oname(otmp, ONAME(obj));
         obj_extract_self(obj);
 
+        if (obj->pickup_prev && otmp->where == OBJ_INVENT)
+            otmp->pickup_prev = 1;
+
         /* really should merge the timeouts */
         if (obj->lamplit)
             obj_merge_light_sources(obj, otmp);
@@ -877,6 +880,11 @@ addinv_core0(struct obj *obj, struct obj *other_obj,
     obj_was_thrown = obj->was_thrown;
     obj->was_thrown = 0;       /* not meaningful for invent */
 
+    if (g.loot_reset_justpicked) {
+        g.loot_reset_justpicked = FALSE;
+        reset_justpicked(g.invent);
+    }
+
     addinv_core1(obj);
 
     /* for addinv_before(); if something has been removed and is now being
@@ -937,6 +945,7 @@ addinv_core0(struct obj *obj, struct obj *other_obj,
         && (throwing_weapon(obj) || is_ammo(obj)))
         setuqwep(obj);
  added:
+    obj->pickup_prev = 1;
     addinv_core2(obj);
     carry_obj_effects(obj); /* carrying affects the obj */
     if (update_perm_invent)
@@ -1164,6 +1173,7 @@ void
 freeinv(struct obj *obj)
 {
     extract_nobj(obj, &g.invent);
+    obj->pickup_prev = 0;
     freeinv_core(obj);
     update_inventory();
 }
@@ -1869,7 +1879,7 @@ ggetobj(const char *word, int (*fn)(OBJ_P), int mx,
     boolean takeoff, ident, allflag, m_seen;
     int itemcount;
     int oletct, iletct, unpaid, oc_of_sym;
-    char sym, *ip, olets[MAXOCLASSES + 5], ilets[MAXOCLASSES + 10];
+    char sym, *ip, olets[MAXOCLASSES + 6], ilets[MAXOCLASSES + 11];
     char extra_removeables[3 + 1]; /* uwep,uswapwep,uquiver */
     char buf[BUFSZ] = DUMMY, qbuf[QBUFSZ];
 
@@ -1908,6 +1918,8 @@ ggetobj(const char *word, int (*fn)(OBJ_P), int mx,
             ilets[iletct++] = 'C';
         if (count_buc(g.invent, BUC_UNKNOWN, ofilter))
             ilets[iletct++] = 'X';
+        if (count_justpicked(g.invent))
+            ilets[iletct++] = 'P';
         ilets[iletct++] = 'a';
     }
     ilets[iletct++] = 'i';
@@ -1988,8 +2000,8 @@ ggetobj(const char *word, int (*fn)(OBJ_P), int mx,
         } else if (sym == 'u') {
             add_valid_menu_class('u');
             ckfn = ckunpaid;
-        } else if (index("BUCX", sym)) {
-            add_valid_menu_class(sym); /* 'B','U','C',or 'X' */
+        } else if (index("BUCXP", sym)) {
+            add_valid_menu_class(sym); /* 'B','U','C','X', or 'P' */
             ckfn = ckvalidcat;
         } else if (sym == 'm') {
             m_seen = TRUE;
@@ -2916,7 +2928,7 @@ count_buc(struct obj *list, int type, boolean (*filterfunc)(OBJ_P))
    rather than looking for a specific type */
 void
 tally_BUCX(struct obj *list, boolean by_nexthere,
-           int *bcp, int *ucp, int *ccp, int *xcp, int *ocp)
+           int *bcp, int *ucp, int *ccp, int *xcp, int *ocp, int *jcp)
 {
     /* Future extensions:
      *  Skip current_container when list is invent, uchain when
@@ -2937,6 +2949,8 @@ tally_BUCX(struct obj *list, boolean by_nexthere,
                 ++(*ucp);
             continue;
         }
+        if (list->pickup_prev)
+            ++(*jcp);
         /* ordinary items */
         if (!list->bknown)
             ++(*xcp);
@@ -3109,6 +3123,9 @@ this_type_only(struct obj *obj)
         case 'X':
             res = !obj->bknown;
             break;
+        case 'P':
+            res = obj->pickup_prev;
+            break;
         default:
             break; /* use 'res' as-is */
         }
@@ -3124,7 +3141,7 @@ dotypeinv(void)
     int n, i = 0;
     char *extra_types, types[BUFSZ];
     int class_count, oclass, unpaid_count, itemcount;
-    int bcnt, ccnt, ucnt, xcnt, ocnt;
+    int bcnt, ccnt, ucnt, xcnt, ocnt, jcnt;
     boolean billx = *u.ushops && doinvbill(0);
     menu_item *pick_list;
     boolean traditional = TRUE;
@@ -3135,7 +3152,7 @@ dotypeinv(void)
         return 0;
     }
     unpaid_count = count_unpaid(g.invent);
-    tally_BUCX(g.invent, FALSE, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt);
+    tally_BUCX(g.invent, FALSE, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt, &jcnt);
 
     if (flags.menu_style != MENU_TRADITIONAL) {
         if (flags.menu_style == MENU_FULL
@@ -3152,6 +3169,8 @@ dotypeinv(void)
                 i |= BUC_CURSED;
             if (xcnt)
                 i |= BUC_UNKNOWN;
+            if (jcnt)
+                i |= JUSTPICKED;
             i |= INCLUDE_VENOM;
             n = query_category(prompt, g.invent, i, &pick_list, PICK_ONE);
             if (!n)
@@ -3180,6 +3199,8 @@ dotypeinv(void)
             types[class_count++] = 'C';
         if (xcnt)
             types[class_count++] = 'X';
+        if (jcnt)
+            types[class_count++] = 'P';
         types[class_count] = '\0';
         /* add everything not already included; user won't see these */
         extra_types = eos(types);
@@ -3236,7 +3257,7 @@ dotypeinv(void)
         return 0;
     }
     if (traditional) {
-        if (index("BUCX", c))
+        if (index("BUCXP", c))
             oclass = c; /* not a class but understood by this_type_only() */
         else
             oclass = def_char_to_objclass(c); /* change to object class */
@@ -3260,6 +3281,9 @@ dotypeinv(void)
             case 'X':
                 after = " whose blessed/uncursed/cursed status is unknown";
                 break; /* better phrasing is desirable */
+            case 'P':
+                after = " you just picked up";
+                break;
             default:
                 /* 'c' is an object class, because we've already handled
                    all the non-class letters which were put into 'types[]';
index 619ec33aeb6c355603b77f0a3517b92166672d77..1ee4d00e943fa6f7c725b7e261ab600d0898db7f 100644 (file)
@@ -400,6 +400,7 @@ splitobj(struct obj *obj, long num)
     otmp->quan = num;
     otmp->owt = weight(otmp); /* -= obj->owt ? */
     otmp->lua_ref_cnt = 0;
+    otmp->pickup_prev = 0;
 
     g.context.objsplit.parent_oid = obj->o_id;
     g.context.objsplit.child_oid = otmp->o_id;
@@ -765,6 +766,7 @@ mksobj(int otyp, boolean init, boolean artif)
     unknow_object(otmp); /* set up dknown and known: non-0 for some things */
     otmp->corpsenm = NON_PM;
     otmp->lua_ref_cnt = 0;
+    otmp->pickup_prev = 0;
 
     if (init) {
         switch (let) {
index 49f5c3a05ddaa898a263999e3f8ffce54aae82e2..efa8027cadd6c1609599aaae63b8beea90b2f5ac 100644 (file)
@@ -46,6 +46,7 @@ static boolean able_to_loot(int, int, boolean);
 static boolean reverse_loot(void);
 static boolean mon_beside(int, int);
 static int do_loot_cont(struct obj **, int, int);
+static int doloot_core(void);
 static void tipcontainer(struct obj *);
 
 /* define for query_objlist() and autopickup() */
@@ -133,7 +134,7 @@ query_classes(char oclasses[], boolean *one_at_a_time, boolean *everything,
     boolean not_everything, filtered;
     char qbuf[QBUFSZ];
     boolean m_seen;
-    int itemcount, bcnt, ucnt, ccnt, xcnt, ocnt;
+    int itemcount, bcnt, ucnt, ccnt, xcnt, ocnt, jcnt;
 
     oclasses[oclassct = 0] = '\0';
     *one_at_a_time = *everything = m_seen = FALSE;
@@ -159,7 +160,7 @@ query_classes(char oclasses[], boolean *one_at_a_time, boolean *everything,
     if (count_unpaid(objs))
         ilets[iletct++] = 'u';
 
-    tally_BUCX(objs, here, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt);
+    tally_BUCX(objs, here, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt, &jcnt);
     if (bcnt)
         ilets[iletct++] = 'B';
     if (ucnt)
@@ -168,6 +169,8 @@ query_classes(char oclasses[], boolean *one_at_a_time, boolean *everything,
         ilets[iletct++] = 'C';
     if (xcnt)
         ilets[iletct++] = 'X';
+    if (jcnt)
+        ilets[iletct++] = 'P';
     ilets[iletct] = '\0';
 
     if (iletct > 1) {
@@ -203,8 +206,8 @@ query_classes(char oclasses[], boolean *one_at_a_time, boolean *everything,
                 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' */
+            } else if (index("uBUCXP", sym)) {
+                add_valid_menu_class(sym); /* 'u' or 'B','U','C','X','P' */
                 filtered = TRUE;
             } else {
                 oc_of_sym = def_char_to_objclass(sym);
@@ -407,6 +410,7 @@ add_valid_menu_class(int c)
     if (c == 0) { /* reset */
         vmc_count = 0;
         g.class_filter = g.bucx_filter = g.shop_filter = FALSE;
+        g.picked_filter = FALSE;
     } else if (!menu_class_present(c)) {
         g.valid_menu_classes[vmc_count++] = (char) c;
         /* categorize the new class */
@@ -417,6 +421,9 @@ add_valid_menu_class(int c)
         case 'X':
             g.bucx_filter = TRUE;
             break;
+        case 'P':
+            g.picked_filter = TRUE;
+            break;
         case 'u':
             g.shop_filter = TRUE;
             break;
@@ -457,6 +464,8 @@ allow_category(struct obj *obj)
                  ? (index(g.valid_menu_classes, COIN_CLASS) ? TRUE : FALSE)
                  : g.shop_filter /* coins are never unpaid, but check anyway */
                     ? (obj->unpaid ? TRUE : FALSE)
+            : g.picked_filter
+            ? obj->pickup_prev
                     : g.bucx_filter
                        ? (index(g.valid_menu_classes, flags.goldX ? 'X' : 'U')
                           ? TRUE : FALSE)
@@ -500,6 +509,8 @@ allow_category(struct obj *obj)
         if (!index(g.valid_menu_classes, bucx))
             return FALSE;
     }
+    if (g.picked_filter && !obj->pickup_prev)
+        return FALSE;
     /* obj didn't fail any of the filter checks, so accept */
     return TRUE;
 }
@@ -524,6 +535,41 @@ is_worn_by_type(struct obj *otmp)
     return (is_worn(otmp) && allow_category(otmp)) ? TRUE : FALSE;
 }
 
+/* reset last-picked-up flags */
+void
+reset_justpicked(struct obj *olist)
+{
+    struct obj *otmp;
+
+    for (otmp = olist; otmp; otmp = otmp->nobj)
+        otmp->pickup_prev = 0;
+}
+
+int
+count_justpicked(struct obj *olist)
+{
+    struct obj *otmp;
+    int cnt = 0;
+
+    for (otmp = olist; otmp; otmp = otmp->nobj)
+        if (otmp->pickup_prev)
+            cnt++;
+
+    return cnt;
+}
+
+struct obj *
+find_justpicked(struct obj *olist)
+{
+    struct obj *otmp;
+
+    for (otmp = olist; otmp; otmp = otmp->nobj)
+        if (otmp->pickup_prev)
+            return otmp;
+
+    return (struct obj *) 0;
+}
+
 /*
  * Have the hero pick things from the ground
  * or a monster's inventory if swallowed.
@@ -601,6 +647,7 @@ pickup(int what) /* should be a long */
             nomul(0);
     }
 
+    reset_justpicked(g.invent);
     add_valid_menu_class(0); /* reset */
     if (!u.uswallow) {
         objchain_p = &g.level.objects[u.ux][u.uy];
@@ -1083,6 +1130,7 @@ query_category(const char *qstr,      /* query string */
             do_buc_unknown = FALSE;
     int num_buc_types = 0;
     unsigned itemflags = MENU_ITEMFLAGS_NONE;
+    int num_justpicked = 0;
 
     *pick_list = (menu_item *) 0;
     if (!olist)
@@ -1107,6 +1155,9 @@ query_category(const char *qstr,      /* query string */
         do_buc_unknown = TRUE;
         num_buc_types++;
     }
+    if (qflags & JUSTPICKED) {
+        num_justpicked = count_justpicked(olist);
+    }
 
     ccount = count_categories(olist, qflags);
     /* no point in actually showing a menu for a single category */
@@ -1246,6 +1297,19 @@ query_category(const char *qstr,      /* query string */
         add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE,
                  "Items of unknown Bless/Curse status", itemflags);
     }
+    if (num_justpicked) {
+        char tmpbuf[BUFSZ];
+
+        if (num_justpicked == 1)
+            Sprintf(tmpbuf, "%s", doname(find_justpicked(olist)));
+        else
+            Sprintf(tmpbuf, "Items you just picked up");
+        invlet = 'P';
+        any = cg.zeroany;
+        any.a_int = 'P';
+        add_menu(win, &nul_glyphinfo, &any, invlet, 0, ATR_NONE,
+                 tmpbuf, itemflags);
+    }
     end_menu(win, qstr);
     n = select_menu(win, how, pick_list);
  query_done:
@@ -1834,9 +1898,21 @@ do_loot_cont(struct obj **cobjp,
     return use_container(cobjp, 0, (boolean) (cindex < ccount));
 }
 
-/* loot a container on the floor or loot saddle from mon. */
+/* #loot extended command */
 int
 doloot(void)
+{
+    int res;
+
+    g.loot_reset_justpicked = TRUE;
+    res = doloot_core();
+    g.loot_reset_justpicked = FALSE;
+    return res;
+}
+
+/* loot a container on the floor or loot saddle from mon. */
+static int
+doloot_core(void)
 {
     struct obj *cobj, *nobj;
     register int c = -1;
@@ -2901,6 +2977,7 @@ menu_loot(int retry, boolean put_in)
     int n, i, n_looted = 0;
     boolean all_categories = TRUE, loot_everything = FALSE, autopick = FALSE;
     char buf[BUFSZ];
+    boolean loot_justpicked = FALSE;
     const char *action = put_in ? "Put in" : "Take out";
     struct obj *otmp, *otmp2;
     menu_item *pick_list;
@@ -2912,7 +2989,8 @@ menu_loot(int retry, boolean put_in)
     } else if (flags.menu_style == MENU_FULL) {
         all_categories = FALSE;
         Sprintf(buf, "%s what type of objects?", action);
-        mflags = (ALL_TYPES | UNPAID_TYPES | BUCX_TYPES | CHOOSE_ALL);
+        mflags = (ALL_TYPES | UNPAID_TYPES | BUCX_TYPES | CHOOSE_ALL
+                  | JUSTPICKED );
         n = query_category(buf, put_in ? g.invent : g.current_container->cobj,
                            mflags, &pick_list, PICK_ANY);
         if (!n)
@@ -2920,6 +2998,10 @@ menu_loot(int retry, boolean put_in)
         for (i = 0; i < n; i++) {
             if (pick_list[i].item.a_int == 'A') {
                 loot_everything = autopick = TRUE;
+            } else if (put_in && pick_list[i].item.a_int == 'P') {
+                loot_justpicked = TRUE;
+                count = max(0, pick_list[i].count);
+                add_valid_menu_class(pick_list[i].item.a_int);
             } else if (pick_list[i].item.a_int == ALL_TYPES_SELECTED) {
                 all_categories = TRUE;
             } else {
@@ -2958,10 +3040,22 @@ menu_loot(int retry, boolean put_in)
                 n_looted += res;
             }
         }
+    } else if (put_in && loot_justpicked && count_justpicked(g.invent) == 1) {
+        otmp = find_justpicked(g.invent);
+        if (otmp) {
+            n_looted = 1;
+            if (count > 0 && count < otmp->quan) {
+                otmp = splitobj(otmp, count);
+            }
+            (void) in_container(otmp);
+            /* return value doesn't matter, even if container blew up */
+        }
     } else {
         mflags = INVORDER_SORT | INCLUDE_VENOM;
         if (put_in && flags.invlet_constant)
             mflags |= USE_INVLET;
+        if (put_in && loot_justpicked)
+            mflags |= JUSTPICKED;
         if (!put_in)
             g.current_container->cknown = 1;
         Sprintf(buf, "%s what?", action);