BUCX filtering

The different menustyle settings have been offering different degrees
of support for BUCX filtering:
  Full       : multi-drop, container-in, container-out
  Trad, Combo: multi-drop
  Partial    : none (to be expected; it explicitly jumps past class
    filtering, where BUCX gets handled, to a menu of all objects)

This adds pickup, container-in, container-out, multi-unwear/unwield,
and object-ID for Trad and Combo, and multi-unwear/unwield for Full.
(Full behaves like Partial for pickup--not sure why--and for object-ID,
bypassing filters to go straight to a menu of all applicable items.)

There are probably several new bugs--this stuff is very convoluted....
This commit is contained in:
PatR
2017-06-23 18:44:35 -07:00
parent 6a84840971
commit e32c09c5bf
6 changed files with 134 additions and 85 deletions

View File

@@ -400,6 +400,10 @@ named floating eye (when hit by another monster with reflection) or named
message formatting and conceivably trigger crash if name had '%' in it
fix "you feel like a new man" if female human hero's polymorph attempt failed
while in human form (when already poly'd, "new woman" correctly shown)
fix the 'A' command to have the 'D' command's fix for C331-1 (quirk for
menustyle:Combination; if user included 'a' in "which object classes?"
response, to operate on applicable all items, there would still be a
followup menu asking to choose specific items)
Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository
@@ -584,6 +588,10 @@ wizard-mode command #wizmakemap to recreate the current level
'goldX' boolean option to treat gold pieces as X (vs U) during BUCX filtering
(should be persistent but is reset each save/restore cycle in order
to avoid breaking 3.6.0 save files)
for menustyle:Traditional and Combination, support BUCX filtering for item
pick-up and container put-in and take-out; also for object IDing
for menustyle:Full and Traditional and Combination, support BUCX filtering
for the 'A' command
Platform- and/or Interface-Specific New Features

View File

@@ -983,7 +983,9 @@ E void NDECL(reassign);
E int NDECL(doorganize);
E void NDECL(free_pickinv_cache);
E int FDECL(count_unpaid, (struct obj *));
E int FDECL(count_buc, (struct obj *, int));
E int FDECL(count_buc, (struct obj *, int, boolean (*)(OBJ_P)));
E void FDECL(tally_BUCX, (struct obj *, BOOLEAN_P,
int *, int *, int *, int *, int *));
E long FDECL(count_contents, (struct obj *, BOOLEAN_P, BOOLEAN_P, BOOLEAN_P));
E void FDECL(carry_obj_effects, (struct obj *));
E const char *FDECL(currency, (long));
@@ -1780,6 +1782,7 @@ E void NDECL(getlock);
E int FDECL(collect_obj_classes, (char *, struct obj *, BOOLEAN_P,
boolean FDECL((*), (OBJ_P)), int *));
E boolean FDECL(rider_corpse_revival, (struct obj *, BOOLEAN_P));
E boolean FDECL(menu_class_present, (int));
E void FDECL(add_valid_menu_class, (int));
E boolean FDECL(allow_all, (struct obj *));
E boolean FDECL(allow_category, (struct obj *));

View File

@@ -280,6 +280,7 @@ enum hmon_atkmode_types {
#define BUC_UNCURSED 0x200
#define BUC_UNKNOWN 0x400
#define BUC_ALLBKNOWN (BUC_BLESSED | BUC_CURSED | BUC_UNCURSED)
#define BUCX_TYPES (BUC_ALLBKNOWN | BUC_UNKNOWN)
#define ALL_TYPES_SELECTED -2
/* Flags to control find_mid() */

View File

@@ -2541,8 +2541,9 @@ int retry;
} else if (flags.menu_style == MENU_FULL) {
all_worn_categories = FALSE;
n = query_category("What type of things do you want to take off?",
invent, WORN_TYPES | ALL_TYPES, &pick_list,
PICK_ANY);
invent, (WORN_TYPES | ALL_TYPES
| UNPAID_TYPES | BUCX_TYPES),
&pick_list, PICK_ANY);
if (!n)
return 0;
for (i = 0; i < n; i++) {
@@ -2553,10 +2554,17 @@ int retry;
}
free((genericptr_t) pick_list);
} else if (flags.menu_style == MENU_COMBINATION) {
all_worn_categories = FALSE;
if (ggetobj("take off", select_off, 0, TRUE, (unsigned *) 0) == -2)
all_worn_categories = TRUE;
unsigned ggofeedback = 0;
i = ggetobj("take off", select_off, 0, TRUE, &ggofeedback);
if (ggofeedback & ALL_FINISHED)
return 0;
all_worn_categories = (i == -2);
}
if (menu_class_present('u')
|| menu_class_present('B') || menu_class_present('U')
|| menu_class_present('C') || menu_class_present('X'))
all_worn_categories = FALSE;
n = query_objlist("What do you want to take off?", &invent,
(SIGNAL_NOMENU | USE_INVLET | INVORDER_SORT),

View File

@@ -25,8 +25,6 @@ STATIC_PTR char *FDECL(safeq_shortxprname, (struct obj *));
STATIC_DCL char FDECL(display_pickinv, (const char *, const char *,
BOOLEAN_P, long *));
STATIC_DCL char FDECL(display_used_invlets, (CHAR_P));
STATIC_DCL void FDECL(tally_BUCX, (struct obj *,
int *, int *, int *, int *, int *));
STATIC_DCL boolean FDECL(this_type_only, (struct obj *));
STATIC_DCL void NDECL(dounpaid);
STATIC_DCL struct obj *FDECL(find_unpaid, (struct obj *, struct obj **));
@@ -1560,7 +1558,7 @@ unsigned *resultflags;
boolean takeoff, ident, allflag, m_seen;
int itemcount;
int oletct, iletct, unpaid, oc_of_sym;
char sym, *ip, olets[MAXOCLASSES + 5], ilets[MAXOCLASSES + 5];
char sym, *ip, olets[MAXOCLASSES + 5], ilets[MAXOCLASSES + 10];
char extra_removeables[3 + 1]; /* uwep,uswapwep,uquiver */
char buf[BUFSZ], qbuf[QBUFSZ];
@@ -1587,22 +1585,19 @@ unsigned *resultflags;
if (ident && !iletct) {
return -1; /* no further identifications */
} else if (!takeoff && (unpaid || invent)) {
} else if (invent) {
ilets[iletct++] = ' ';
if (unpaid)
ilets[iletct++] = 'u';
if (count_buc(invent, BUC_BLESSED))
if (count_buc(invent, BUC_BLESSED, ofilter))
ilets[iletct++] = 'B';
if (count_buc(invent, BUC_UNCURSED))
if (count_buc(invent, BUC_UNCURSED, ofilter))
ilets[iletct++] = 'U';
if (count_buc(invent, BUC_CURSED))
if (count_buc(invent, BUC_CURSED, ofilter))
ilets[iletct++] = 'C';
if (count_buc(invent, BUC_UNKNOWN))
if (count_buc(invent, BUC_UNKNOWN, ofilter))
ilets[iletct++] = 'X';
if (invent)
ilets[iletct++] = 'a';
} else if (takeoff && invent) {
ilets[iletct++] = ' ';
ilets[iletct++] = 'a';
}
ilets[iletct++] = 'i';
if (!combo)
@@ -1684,17 +1679,8 @@ unsigned *resultflags;
} else if (sym == 'u') {
add_valid_menu_class('u');
ckfn = ckunpaid;
} else if (sym == 'B') {
add_valid_menu_class('B');
ckfn = ckvalidcat;
} else if (sym == 'U') {
add_valid_menu_class('U');
ckfn = ckvalidcat;
} else if (sym == 'C') {
add_valid_menu_class('C');
ckfn = ckvalidcat;
} else if (sym == 'X') {
add_valid_menu_class('X');
} else if (index("BUCX", sym)) {
add_valid_menu_class(sym); /* 'B','U','C',or 'X' */
ckfn = ckvalidcat;
} else if (sym == 'm') {
m_seen = TRUE;
@@ -1751,7 +1737,7 @@ int FDECL((*fn), (OBJ_P)), FDECL((*ckfn), (OBJ_P));
struct obj *otmp, *otmpo;
register char sym, ilet;
register int cnt = 0, dud = 0, tmp;
boolean takeoff, nodot, ident, take_out, put_in, first, ininv;
boolean takeoff, nodot, ident, take_out, put_in, first, ininv, bycat;
char qbuf[QBUFSZ], qpfx[QBUFSZ];
takeoff = taking_off(word);
@@ -1761,6 +1747,8 @@ int FDECL((*fn), (OBJ_P)), FDECL((*ckfn), (OBJ_P));
nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") || ident
|| takeoff || take_out || put_in);
ininv = (*objchn == invent);
bycat = (menu_class_present('B') || menu_class_present('U')
|| menu_class_present('C') || menu_class_present('X'));
/* someday maybe we'll sort by 'olets' too (temporarily replace
flags.packorder and pass SORTLOOT_PACK), but not yet... */
@@ -1792,6 +1780,8 @@ nextclass:
while ((otmp = nxt_unbypassed_obj(*objchn)) != 0) {
if (ilet == 'z')
ilet = 'A';
else if (ilet == 'Z')
ilet = NOINVSYM; /* '#' */
else
ilet++;
if (olets && *olets && otmp->oclass != *olets)
@@ -1802,6 +1792,8 @@ nextclass:
continue;
if (ckfn && !(*ckfn)(otmp))
continue;
if (bycat && !ckvalidcat(otmp))
continue;
if (!allflag) {
safeq_xprn_ctx.let = ilet;
safeq_xprn_ctx.dot = !nodot;
@@ -2405,23 +2397,27 @@ struct obj *list;
* at some point: bknown is forced for priest[ess], like in xname().
*/
int
count_buc(list, type)
count_buc(list, type, filterfunc)
struct obj *list;
int type;
boolean FDECL((*filterfunc), (OBJ_P));
{
int count = 0;
for (; list; list = list->nobj) {
/* priests always know bless/curse state */
if (Role_if(PM_PRIEST))
list->bknown = (list->oclass != COIN_CLASS);
/* some actions exclude some or most items */
if (filterfunc && !(*filterfunc)(list))
continue;
/* coins are either uncursed or unknown based upon option setting */
if (list->oclass == COIN_CLASS) {
if (type == (iflags.goldX ? BUC_UNKNOWN : BUC_UNCURSED))
++count;
continue;
}
/* priests always know bless/curse state */
if (Role_if(PM_PRIEST))
list->bknown = 1;
/* check whether this object matches the requested type */
if (!list->bknown
? (type == BUC_UNKNOWN)
@@ -2435,20 +2431,23 @@ int type;
/* similar to count_buc(), but tallies all states at once
rather than looking for a specific type */
STATIC_OVL void
tally_BUCX(list, bcp, ucp, ccp, xcp, ocp)
void
tally_BUCX(list, by_nexthere, bcp, ucp, ccp, xcp, ocp)
struct obj *list;
boolean by_nexthere;
int *bcp, *ucp, *ccp, *xcp, *ocp;
{
/* Future extensions:
* Add parameter for list traversal by either nexthere or nobj.
* Skip current_container when list is invent, uchain when
* first object of list is located on the floor. 'ocp' will then
* have a function again (it was a counter for having skipped gold,
* but that's not skipped anymore).
*/
*bcp = *ucp = *ccp = *xcp = *ocp = 0;
for (; list; list = list->nobj) {
for ( ; list; list = (by_nexthere ? list->nexthere : list->nobj)) {
/* priests always know bless/curse state */
if (Role_if(PM_PRIEST))
list->bknown = (list->oclass != COIN_CLASS);
/* coins are either uncursed or unknown based upon option setting */
if (list->oclass == COIN_CLASS) {
if (iflags.goldX)
@@ -2457,10 +2456,7 @@ int *bcp, *ucp, *ccp, *xcp, *ocp;
++(*ucp);
continue;
}
/* priests always know bless/curse state */
if (Role_if(PM_PRIEST))
list->bknown = 1;
/* ordinary items */
if (!list->bknown)
++(*xcp);
else if (list->blessed)
@@ -2642,7 +2638,7 @@ dotypeinv()
return 0;
}
unpaid_count = count_unpaid(invent);
tally_BUCX(invent, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt);
tally_BUCX(invent, FALSE, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt);
if (flags.menu_style != MENU_TRADITIONAL) {
if (flags.menu_style == MENU_FULL

View File

@@ -149,45 +149,54 @@ struct obj *objs;
boolean here;
int *menu_on_demand;
{
char ilets[30], inbuf[BUFSZ]; /* FIXME: hardcoded ilets[] length */
char ilets[36], inbuf[BUFSZ]; /* 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;
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';
}
if (itemcount && menu_on_demand)
ilets[iletct++] = 'm';
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;
not_everything = filtered = FALSE;
Sprintf(qbuf, "What kinds of thing do you want to %s? [%s]", action,
ilets);
getlin(qbuf, inbuf);
@@ -213,6 +222,9 @@ int *menu_on_demand;
goto ask_again;
} else if (sym == 'm') {
m_seen = TRUE;
} else if (index("BUCX", sym)) {
add_valid_menu_class(sym); /* 'B','U','C',or 'X' */
filtered = TRUE;
} else {
oc_of_sym = def_char_to_objclass(sym);
if (index(ilets, sym)) {
@@ -230,9 +242,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)) {
@@ -325,6 +339,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;
@@ -334,7 +356,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) {
@@ -441,7 +463,7 @@ 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;
return FALSE;
@@ -453,8 +475,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;
}
/*
@@ -586,8 +607,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) */
@@ -616,7 +637,7 @@ int what; /* should be a long */
(traverse_how & BY_NEXTHERE) ? TRUE : FALSE,
&via_menu)) {
if (!via_menu)
return 0;
goto pickupdone;
if (selective)
traverse_how |= INVORDER_SORT;
n = query_objlist("Pick up what?", objchain_p, traverse_how,
@@ -626,10 +647,14 @@ int what; /* should be a long */
goto menu_pickup;
}
}
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 (!selective && oclasses[0] && !index(oclasses, obj->oclass))
if (bycat ? !allow_category(obj)
: (!selective && oclasses[0]
&& !index(oclasses, obj->oclass)))
continue;
lcount = -1L;
@@ -686,6 +711,8 @@ int what; /* should be a long */
if (autopickup)
check_here(n_picked > 0);
}
pickupdone:
add_valid_menu_class(0); /* reset */
return (n_tried > 0);
}
@@ -981,6 +1008,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;
@@ -991,19 +1019,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++;
}
@@ -1013,8 +1043,7 @@ 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;
}
@@ -1045,8 +1074,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;
@@ -1093,7 +1121,9 @@ int how; /* type of query */
: "Auto-select every item",
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;
@@ -1120,7 +1150,7 @@ int how; /* type of query */
any = zeroany;
any.a_int = 'X';
add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
"Items of unknown B/C/U status", MENU_UNSELECTED);
"Items of unknown Bless/Curse status", MENU_UNSELECTED);
}
end_menu(win, qstr);
n = select_menu(win, how, pick_list);
@@ -2507,6 +2537,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */
used |= traditional_loot(FALSE);
else
used |= (menu_loot(0, FALSE) > 0);
add_valid_menu_class(0);
}
}
@@ -2527,6 +2558,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */
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) {
@@ -2556,6 +2588,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */
used |= traditional_loot(FALSE);
else
used |= (menu_loot(0, FALSE) > 0);
add_valid_menu_class(0);
}
}
@@ -2585,7 +2618,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;
@@ -2633,7 +2666,7 @@ boolean put_in;
} else if (flags.menu_style == MENU_FULL) {
all_categories = FALSE;
Sprintf(buf, "%s what type of objects?", action);
mflags = (ALL_TYPES | BUC_ALLBKNOWN | BUC_UNKNOWN);
mflags = (ALL_TYPES | BUCX_TYPES);
if (put_in)
mflags |= CHOOSE_ALL;
n = query_category(buf, put_in ? invent : current_container->cobj,