From d7d1c1476de58e70742b7c58cdf10d10072f8035 Mon Sep 17 00:00:00 2001 From: Michael Meyer Date: Thu, 16 Nov 2023 09:57:31 -0500 Subject: [PATCH] Add option to exclude dropped items from autopick This is based on a feature in UnNetHack (and I think some other variants as well). If the hero intentionally drops an item with 'pickup_dropped' disabled, don't autopick it back up when walking over that square again. Typically when the player drops an item, it's because she doesn't want it in her inventory any more, and this option stops autopickup from defeating that goal (especially useful for tasks like stash management without a container). Players have come up with workarounds to this problem like toggling autopickup when approaching their stash pile or adding name-based autopickup exceptions to allow them to exclude individual items from autopickup, but this behavior should reduce the need for those things. I think 'pickup_dropped' is a little unfortunate because it suggests equivalence to 'pickup_thrown' (i.e. any dropped items will be automatically picked up regardless of autopickup exceptions). Calling it something like 'nopick_dropped' might be better, but as far as I can tell options cannot start with the word 'no' because it's interpreted as a negation of the rest of the option name. --- dat/opthelp | 1 + doc/Guidebook.mn | 11 +++++++++++ doc/Guidebook.tex | 9 +++++++++ include/flag.h | 1 + include/obj.h | 5 +++-- include/optlist.h | 3 +++ src/bones.c | 1 + src/do.c | 1 + src/invent.c | 2 +- src/options.c | 3 ++- src/pickup.c | 11 ++++++++--- 11 files changed, 41 insertions(+), 7 deletions(-) diff --git a/dat/opthelp b/dat/opthelp index 938debeda..73945261a 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -50,6 +50,7 @@ null allow nulls to be sent to your terminal [True] delay code) if moving objects seem to teleport across rooms perm_invent keep inventory in a permanent window [False] pickup_thrown override pickup_types for thrown objects [True] +pickup_dropped evaluate pickup_types for dropped objects [True] pushweapon when wielding a new weapon, put your previously [False] wielded weapon into the secondary weapon slot quick_farsight usually skip the chance to browse the map when [False] diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index d778e72cb..16e45fa54 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4387,6 +4387,17 @@ level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or overLoaded), you will be asked if you want to continue. (Default \(oqS\(cq). Persistent. +.lp pickup_dropped +If this option is on, items you dropped will be treated like normal items for +.op autopickup +purposes. If it is disabled, items you dropped will not be automatically +picked up, even if +.op autopickup +is on and they are in +.op pickup_types +or match a positive autopickup exception. +Default is on. +Persistent. .lp pickup_thrown If this option is on and .op autopickup diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 116ef004f..7f74e62b2 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -4809,6 +4809,15 @@ level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, or overLoaded), you will be asked if you want to continue. (Default `S'). Persistent. %.lp +\item[\ib{pickup\verb+_+dropped}] +If this option is on, items you dropped will be treated like normal items for +``{\it autopickup\/}'' purposes. If it disabled, items you dropped will not be +automatically picked up, even if ``{\it autopickup\/}'' is on and they are in +``{\it pickup\verb+_+types\/}'' or +match an autopickup exception. +Default is on. +Persistent. +%.lp \item[\ib{pickup\verb+_+thrown}] If this option is on and ``{\it autopickup\/}'' is also on, try to pick up things that you threw, even if they aren't in diff --git a/include/flag.h b/include/flag.h index 19771a830..8fd5a07bb 100644 --- a/include/flag.h +++ b/include/flag.h @@ -47,6 +47,7 @@ struct flag { boolean nap; /* `timed_delay' option for display effects */ boolean null; /* OK to send nulls to the terminal */ boolean pickup; /* whether you pickup or move and look */ + boolean pickup_dropped; /* items you dropped may be autopicked */ boolean pickup_thrown; /* auto-pickup items you threw */ boolean pushweapon; /* When wielding, push old weapon into second slot */ boolean quick_farsight; /* True disables map browsing during random diff --git a/include/obj.h b/include/obj.h index 5a8de9a7a..7c430c38f 100644 --- a/include/obj.h +++ b/include/obj.h @@ -127,6 +127,7 @@ struct obj { Bitfield(greased, 1); /* covered with grease */ Bitfield(nomerge, 1); /* set temporarily to prevent merging */ Bitfield(was_thrown, 1); /* thrown by hero since last picked up */ + Bitfield(was_dropped, 1); /* dropped deliberately by the hero */ Bitfield(in_use, 1); /* for magic items before useup items */ Bitfield(bypass, 1); /* mark this as an object to be skipped by bhito() */ @@ -144,9 +145,9 @@ struct obj { Bitfield(eknown, 1); /* effect known for wands zapped or rings worn when * not seen yet after being picked up while blind * [maybe for remaining stack of used potion too] */ - /* 0 free bits */ + /* 7 free bits */ #else - /* 2 free bits */ + /* 1 free bit */ #endif int corpsenm; /* type of corpse is mons[corpsenm] */ diff --git a/include/optlist.h b/include/optlist.h index 91c0518ea..16be32b31 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -531,6 +531,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTC(pickup_burden, Advanced, 20, opt_in, set_in_game, No, Yes, No, Yes, NoAlias, "maximum burden picked up before prompt") + NHOPTB(pickup_dropped, Behavior, 0, opt_out, set_in_game, + On, Yes, No, No, NoAlias, &flags.pickup_dropped, Term_False, + "consider dropped items for autopickup") NHOPTB(pickup_thrown, Behavior, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.pickup_thrown, Term_False, "autopickup thrown items") diff --git a/src/bones.c b/src/bones.c index 0abdb4000..c6d79d830 100644 --- a/src/bones.c +++ b/src/bones.c @@ -108,6 +108,7 @@ resetobjs(struct obj *ochain, boolean restore) otmp->invlet = 0; otmp->no_charge = 0; otmp->was_thrown = 0; + otmp->was_dropped = 0; /* strip user-supplied names */ /* Statue and some corpse names are left intact, diff --git a/src/do.c b/src/do.c index f74b36b0c..86a81d8d0 100644 --- a/src/do.c +++ b/src/do.c @@ -760,6 +760,7 @@ drop(struct obj *obj) if (!IS_ALTAR(levl[u.ux][u.uy].typ) && flags.verbose) You("drop %s.", doname(obj)); } + obj->was_dropped = 1; dropx(obj); return ECMD_TIME; } diff --git a/src/invent.c b/src/invent.c index d05c93813..af0322c96 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1050,7 +1050,7 @@ addinv_core0(struct obj *obj, struct obj *other_obj, if (Has_contents(obj)) picked_container(obj); /* clear no_charge */ obj_was_thrown = obj->was_thrown; - obj->was_thrown = 0; /* not meaningful for invent */ + obj->was_thrown = obj->was_dropped = 0; /* not meaningful for invent */ if (gl.loot_reset_justpicked) { gl.loot_reset_justpicked = FALSE; diff --git a/src/options.c b/src/options.c index e8d758d95..57e32d457 100644 --- a/src/options.c +++ b/src/options.c @@ -8593,7 +8593,8 @@ doset_simple_menu(void) /* pickup_types is separated from autopickup due to the spelling of their names; emphasize what it means */ if (allopt[i].idx == opt_pickup_types - || allopt[i].idx == opt_pickup_thrown) + || allopt[i].idx == opt_pickup_thrown + || allopt[i].idx == opt_pickup_dropped) Strcat(buf, " (for autopickup)"); add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, NO_COLOR, buf, MENU_ITEMFLAGS_NONE); diff --git a/src/pickup.c b/src/pickup.c index f1d0a733c..8a531668c 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -904,6 +904,12 @@ autopick_testobj(struct obj *otmp, boolean calc_costly) if (costly && !otmp->no_charge) return FALSE; + /* pickup_thrown/!pickup_dropped override pickup_types and exceptions */ + if (flags.pickup_thrown && otmp->was_thrown) + return TRUE; + if (!flags.pickup_dropped && otmp->was_dropped) + return FALSE; + /* check for pickup_types */ pickit = (!*otypes || strchr(otypes, otmp->oclass)); @@ -912,9 +918,6 @@ autopick_testobj(struct obj *otmp, boolean calc_costly) if (ape) pickit = ape->grab; - /* pickup_thrown overrides pickup_types and exceptions */ - if (!pickit) - pickit = (flags.pickup_thrown && otmp->was_thrown); return pickit; } @@ -3664,6 +3667,7 @@ tipcontainer(struct obj *box) /* or bag */ (void) add_to_container(targetbox, otmp); } } else if (highdrop) { + otmp->was_dropped = 1; /* might break or fall down stairs; handles altars itself */ hitfloor(otmp, TRUE); } else { @@ -3676,6 +3680,7 @@ tipcontainer(struct obj *box) /* or bag */ pline("%s%c", doname(otmp), nobj ? ',' : '.'); iflags.last_msg = PLNMSG_OBJNAM_ONLY; } + otmp->was_dropped = 1; dropy(otmp); if (iflags.last_msg != PLNMSG_OBJNAM_ONLY) terse = FALSE; /* terse formatting has been interrupted */