Add 'pickup_stolen' option

Add pickup_stolen option to autopick items stolen from you by a nymph or
monkey, even if they don't match your normal autopickup settings.
Replace was_dropped, was_thrown with a 2-bit bitfield that can contain
values LOST_DROPPED, LOST_THROWN, and LOST_STOLEN (or 0), since they
should all be mutually exclusive anyway as they track the most recent
way the item left the hero's inventory.

[Rebase/merge conflict fixed up.  PR]
This commit is contained in:
Michael Meyer
2023-12-08 15:19:54 -08:00
committed by PatR
parent ecda85cc32
commit 1736f3caaa
15 changed files with 54 additions and 23 deletions
+1
View File
@@ -50,6 +50,7 @@ null allow nulls to be sent to your terminal [True]
try turning this option off (forcing NetHack to use its own try turning this option off (forcing NetHack to use its own
delay code) if moving objects seem to teleport across rooms delay code) if moving objects seem to teleport across rooms
perm_invent keep inventory in a permanent window [False] perm_invent keep inventory in a permanent window [False]
pickup_stolen override pickup_types for stolen objects [True]
pickup_thrown override pickup_types for thrown objects [True] pickup_thrown override pickup_types for thrown objects [True]
pushweapon when wielding a new weapon, put your previously [False] pushweapon when wielding a new weapon, put your previously [False]
wielded weapon into the secondary weapon slot wielded weapon into the secondary weapon slot
+9
View File
@@ -4394,6 +4394,15 @@ level (Unencumbered, Burdened, streSsed, straiNed, overTaxed,
or overLoaded), you will be asked if you want to continue. or overLoaded), you will be asked if you want to continue.
(Default \(oqS\(cq). (Default \(oqS\(cq).
Persistent. Persistent.
.lp pickup_stolen
If this option is on and
.op autopickup
is also on, try to pick up things that a monster stole from you, even if they
aren't in
.op pickup_types
or match an autopickup exception.
Default is on.
Persistent.
.lp pickup_thrown .lp pickup_thrown
If this option is on and If this option is on and
.op autopickup .op autopickup
+8
View File
@@ -4815,6 +4815,14 @@ level (Unencumbered, Burdened, streSsed, straiNed, overTaxed,
or overLoaded), you will be asked if you want to continue. or overLoaded), you will be asked if you want to continue.
(Default `S'). Persistent. (Default `S'). Persistent.
%.lp %.lp
\item[\ib{pickup\verb+_+stolen}]
If this option is on and ``{\it autopickup\/}'' is also on, try to pick up
things that a monster stole from you, even if they aren't in
``{\it pickup\verb+_+types\/}'' or
match an autopickup exception.
Default is on.
Persistent.
%.lp
\item[\ib{pickup\verb+_+thrown}] \item[\ib{pickup\verb+_+thrown}]
If this option is on and ``{\it autopickup\/}'' is also on, try to pick up 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 things that you threw, even if they aren't in
+1
View File
@@ -48,6 +48,7 @@ struct flag {
boolean nopick_dropped; /* items you dropped may be autopicked */ boolean nopick_dropped; /* items you dropped may be autopicked */
boolean null; /* OK to send nulls to the terminal */ boolean null; /* OK to send nulls to the terminal */
boolean pickup; /* whether you pickup or move and look */ boolean pickup; /* whether you pickup or move and look */
boolean pickup_stolen; /* auto-pickup items stolen by a monster */
boolean pickup_thrown; /* auto-pickup items you threw */ boolean pickup_thrown; /* auto-pickup items you threw */
boolean pushweapon; /* When wielding, push old weapon into second slot */ boolean pushweapon; /* When wielding, push old weapon into second slot */
boolean quick_farsight; /* True disables map browsing during random boolean quick_farsight; /* True disables map browsing during random
+9 -4
View File
@@ -124,10 +124,9 @@ struct obj {
#define on_ice recharged /* corpse on ice */ #define on_ice recharged /* corpse on ice */
Bitfield(lamplit, 1); /* a light-source -- can be lit */ Bitfield(lamplit, 1); /* a light-source -- can be lit */
Bitfield(globby, 1); /* combines with like types on adjacent squares */ Bitfield(globby, 1); /* combines with like types on adjacent squares */
Bitfield(greased, 1); /* covered with grease */ Bitfield(greased, 1); /* covered with grease */
Bitfield(nomerge, 1); /* set temporarily to prevent merging */ Bitfield(nomerge, 1); /* set temporarily to prevent merging */
Bitfield(was_thrown, 1); /* thrown by hero since last picked up */ Bitfield(how_lost, 2); /* stolen by mon or thrown, dropped by hero */
Bitfield(was_dropped, 1); /* dropped deliberately by the hero */
Bitfield(in_use, 1); /* for magic items before useup items */ Bitfield(in_use, 1); /* for magic items before useup items */
Bitfield(bypass, 1); /* mark this as an object to be skipped by bhito() */ Bitfield(bypass, 1); /* mark this as an object to be skipped by bhito() */
@@ -459,6 +458,12 @@ struct obj {
#define POTHIT_MONST_THROW 2 /* thrown by a monster */ #define POTHIT_MONST_THROW 2 /* thrown by a monster */
#define POTHIT_OTHER_THROW 3 /* propelled by some other means [scatter()] */ #define POTHIT_OTHER_THROW 3 /* propelled by some other means [scatter()] */
/* tracking how an item left your inventory */
#define LOST_NONE 0 /* still in inventory, or method not covered below */
#define LOST_THROWN 1 /* thrown or fired by the hero */
#define LOST_DROPPED 2 /* dropped or tipped out of a container by the hero */
#define LOST_STOLEN 3 /* stolen from hero's inventory by a monster */
/* /*
* Notes for adding new oextra structures: * Notes for adding new oextra structures:
* *
+3
View File
@@ -534,6 +534,9 @@ static int optfn_##a(int, int, boolean, char *, char *);
NHOPTC(pickup_burden, Advanced, 20, opt_in, set_in_game, NHOPTC(pickup_burden, Advanced, 20, opt_in, set_in_game,
No, Yes, No, Yes, NoAlias, No, Yes, No, Yes, NoAlias,
"maximum burden picked up before prompt") "maximum burden picked up before prompt")
NHOPTB(pickup_stolen, Behavior, 0, opt_out, set_in_game,
On, Yes, No, No, NoAlias, &flags.pickup_stolen, Term_False,
"autopickup thrown items")
NHOPTB(pickup_thrown, Behavior, 0, opt_out, set_in_game, NHOPTB(pickup_thrown, Behavior, 0, opt_out, set_in_game,
On, Yes, No, No, NoAlias, &flags.pickup_thrown, Term_False, On, Yes, No, No, NoAlias, &flags.pickup_thrown, Term_False,
"autopickup thrown items") "autopickup thrown items")
+1 -2
View File
@@ -107,8 +107,7 @@ resetobjs(struct obj *ochain, boolean restore)
otmp->cknown = 0; otmp->cknown = 0;
otmp->invlet = 0; otmp->invlet = 0;
otmp->no_charge = 0; otmp->no_charge = 0;
otmp->was_thrown = 0; otmp->how_lost = LOST_NONE;
otmp->was_dropped = 0;
/* strip user-supplied names */ /* strip user-supplied names */
/* Statue and some corpse names are left intact, /* Statue and some corpse names are left intact,
+1 -1
View File
@@ -760,7 +760,7 @@ drop(struct obj *obj)
if (!IS_ALTAR(levl[u.ux][u.uy].typ) && flags.verbose) if (!IS_ALTAR(levl[u.ux][u.uy].typ) && flags.verbose)
You("drop %s.", doname(obj)); You("drop %s.", doname(obj));
} }
obj->was_dropped = 1; obj->how_lost = LOST_DROPPED;
dropx(obj); dropx(obj);
return ECMD_TIME; return ECMD_TIME;
} }
+2 -2
View File
@@ -1504,7 +1504,7 @@ throwit(struct obj *obj,
} }
gt.thrownobj = obj; gt.thrownobj = obj;
gt.thrownobj->was_thrown = 1; gt.thrownobj->how_lost = LOST_THROWN;
iflags.returning_missile = AutoReturn(obj, wep_mask) ? (genericptr_t) obj iflags.returning_missile = AutoReturn(obj, wep_mask) ? (genericptr_t) obj
: (genericptr_t) 0; : (genericptr_t) 0;
/* NOTE: No early returns after this point or returning_missile /* NOTE: No early returns after this point or returning_missile
@@ -1719,7 +1719,7 @@ throwit(struct obj *obj,
if (tethered_weapon) if (tethered_weapon)
tmp_at(DISP_END, 0); tmp_at(DISP_END, 0);
/* when this location is stepped on, the weapon will be /* when this location is stepped on, the weapon will be
auto-picked up due to 'obj->was_thrown' of 1; auto-picked up due to 'obj->how_lost' of LOST_THROWN;
addinv() prevents thrown Mjollnir from being placed addinv() prevents thrown Mjollnir from being placed
into the quiver slot, but an aklys will end up there if into the quiver slot, but an aklys will end up there if
that slot is empty at the time; since hero will need to that slot is empty at the time; since hero will need to
+5 -5
View File
@@ -942,7 +942,8 @@ merged(struct obj **potmp, struct obj **pobj)
items, where this would be too spammy as such items get items, where this would be too spammy as such items get
unidentified by monsters very frequently). */ unidentified by monsters very frequently). */
if (discovered && otmp->where == OBJ_INVENT if (discovered && otmp->where == OBJ_INVENT
&& !obj->was_thrown && !otmp->was_thrown) { && obj->how_lost != LOST_THROWN
&& otmp->how_lost != LOST_THROWN) {
pline("You learn more about your items by comparing them."); pline("You learn more about your items by comparing them.");
} }
@@ -1049,8 +1050,8 @@ addinv_core0(struct obj *obj, struct obj *other_obj,
obj->no_charge = 0; /* should not be set in hero's invent */ obj->no_charge = 0; /* should not be set in hero's invent */
if (Has_contents(obj)) if (Has_contents(obj))
picked_container(obj); /* clear no_charge */ picked_container(obj); /* clear no_charge */
obj_was_thrown = obj->was_thrown; obj_was_thrown = (obj->how_lost == LOST_THROWN);
obj->was_thrown = obj->was_dropped = 0; /* not meaningful for invent */ obj->how_lost = LOST_NONE;
if (gl.loot_reset_justpicked) { if (gl.loot_reset_justpicked) {
gl.loot_reset_justpicked = FALSE; gl.loot_reset_justpicked = FALSE;
@@ -4823,8 +4824,7 @@ mergable(
if (obj->unpaid != otmp->unpaid || obj->spe != otmp->spe if (obj->unpaid != otmp->unpaid || obj->spe != otmp->spe
|| obj->no_charge != otmp->no_charge || obj->obroken != otmp->obroken || obj->no_charge != otmp->no_charge || obj->obroken != otmp->obroken
|| obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit || obj->otrapped != otmp->otrapped || obj->lamplit != otmp->lamplit
|| obj->was_thrown != otmp->was_thrown || obj->how_lost != otmp->how_lost)
|| obj->was_dropped != otmp->was_dropped)
return FALSE; return FALSE;
if (obj->oclass == FOOD_CLASS if (obj->oclass == FOOD_CLASS
+4 -3
View File
@@ -2863,9 +2863,10 @@ objlist_sanity(struct obj *objlist, int wheretype, const char *mesg)
for (obj = objlist; obj; obj = obj->nobj) { for (obj = objlist; obj; obj = obj->nobj) {
if (obj->where != wheretype) if (obj->where != wheretype)
insane_object(obj, ofmt0, mesg, (struct monst *) 0); insane_object(obj, ofmt0, mesg, (struct monst *) 0);
if (obj->was_thrown && obj->was_dropped) { if (obj->where == OBJ_INVENT && obj->how_lost != LOST_NONE) {
insane_object(obj, "%s obj is both thrown and dropped! %s %s: %s", char lostbuf[40];
mesg, obj->ocarry); Sprintf(lostbuf, "how_lost=%d obj in inventory!", obj->how_lost);
insane_object(obj, ofmt0, lostbuf, (struct monst *) 0);
} }
if (Has_contents(obj)) { if (Has_contents(obj)) {
if (wheretype == OBJ_ONBILL) if (wheretype == OBJ_ONBILL)
+1 -1
View File
@@ -312,7 +312,7 @@ l_obj_to_table(lua_State *L)
nhl_add_table_entry_int(L, "globby", obj->globby); nhl_add_table_entry_int(L, "globby", obj->globby);
nhl_add_table_entry_int(L, "greased", obj->greased); nhl_add_table_entry_int(L, "greased", obj->greased);
nhl_add_table_entry_int(L, "nomerge", obj->nomerge); nhl_add_table_entry_int(L, "nomerge", obj->nomerge);
nhl_add_table_entry_int(L, "was_thrown", obj->was_thrown); nhl_add_table_entry_int(L, "how_lost", obj->how_lost);
nhl_add_table_entry_int(L, "in_use", obj->in_use); nhl_add_table_entry_int(L, "in_use", obj->in_use);
nhl_add_table_entry_int(L, "bypass", obj->bypass); nhl_add_table_entry_int(L, "bypass", obj->bypass);
nhl_add_table_entry_int(L, "cknown", obj->cknown); nhl_add_table_entry_int(L, "cknown", obj->cknown);
+1
View File
@@ -8594,6 +8594,7 @@ doset_simple_menu(void)
spelling of their names; emphasize what it means */ spelling of their names; emphasize what it means */
if (allopt[i].idx == opt_pickup_types if (allopt[i].idx == opt_pickup_types
|| allopt[i].idx == opt_pickup_thrown || allopt[i].idx == opt_pickup_thrown
|| allopt[i].idx == opt_pickup_stolen
|| allopt[i].idx == opt_dropped_nopick) || allopt[i].idx == opt_dropped_nopick)
Strcat(buf, " (for autopickup)"); Strcat(buf, " (for autopickup)");
add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
+7 -5
View File
@@ -904,10 +904,12 @@ autopick_testobj(struct obj *otmp, boolean calc_costly)
if (costly && !otmp->no_charge) if (costly && !otmp->no_charge)
return FALSE; return FALSE;
/* pickup_thrown/nopick_dropped override pickup_types and exceptions */ /* pickup_thrown/pickup_stolen/nopick_dropped override pickup_types and
if (flags.pickup_thrown && otmp->was_thrown) exceptions */
if ((flags.pickup_thrown && otmp->how_lost == LOST_THROWN)
|| (flags.pickup_stolen && otmp->how_lost == LOST_STOLEN))
return TRUE; return TRUE;
if (flags.nopick_dropped && otmp->was_dropped) if (flags.nopick_dropped && otmp->how_lost == LOST_DROPPED)
return FALSE; return FALSE;
/* check for pickup_types */ /* check for pickup_types */
@@ -3667,7 +3669,7 @@ tipcontainer(struct obj *box) /* or bag */
(void) add_to_container(targetbox, otmp); (void) add_to_container(targetbox, otmp);
} }
} else if (highdrop) { } else if (highdrop) {
otmp->was_dropped = 1; otmp->how_lost = LOST_DROPPED;
/* might break or fall down stairs; handles altars itself */ /* might break or fall down stairs; handles altars itself */
hitfloor(otmp, TRUE); hitfloor(otmp, TRUE);
} else { } else {
@@ -3680,7 +3682,7 @@ tipcontainer(struct obj *box) /* or bag */
pline("%s%c", doname(otmp), nobj ? ',' : '.'); pline("%s%c", doname(otmp), nobj ? ',' : '.');
iflags.last_msg = PLNMSG_OBJNAM_ONLY; iflags.last_msg = PLNMSG_OBJNAM_ONLY;
} }
otmp->was_dropped = 1; otmp->how_lost = LOST_DROPPED;
dropy(otmp); dropy(otmp);
if (iflags.last_msg != PLNMSG_OBJNAM_ONLY) if (iflags.last_msg != PLNMSG_OBJNAM_ONLY)
terse = FALSE; /* terse formatting has been interrupted */ terse = FALSE; /* terse formatting has been interrupted */
+1
View File
@@ -527,6 +527,7 @@ steal(struct monst* mtmp, char* objnambuf)
(void) encumber_msg(); (void) encumber_msg();
could_petrify = (otmp->otyp == CORPSE could_petrify = (otmp->otyp == CORPSE
&& touch_petrifies(&mons[otmp->corpsenm])); && touch_petrifies(&mons[otmp->corpsenm]));
otmp->how_lost = LOST_STOLEN;
(void) mpickobj(mtmp, otmp); /* may free otmp */ (void) mpickobj(mtmp, otmp); /* may free otmp */
if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) { if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) {
minstapetrify(mtmp, TRUE); minstapetrify(mtmp, TRUE);