From: PatR Date: Sat, 24 Jun 2017 01:44:35 +0000 (-0700) Subject: BUCX filtering X-Git-Tag: NetHack-3.6.1_RC01~469 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e32c09c5bfe7fa63fbd699e2ad6a4ba23bcc3b94;p=nethack 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.... --- diff --git a/doc/fixes36.1 b/doc/fixes36.1 index aa43cad83..05a714ec5 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -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 diff --git a/include/extern.h b/include/extern.h index 6b92d680d..2ccbf3240 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/include/hack.h b/include/hack.h index b7cf512c7..dffb26c5c 100644 --- a/include/hack.h +++ b/include/hack.h @@ -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() */ diff --git a/src/do_wear.c b/src/do_wear.c index 71599fd7a..2adcb6816 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -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), diff --git a/src/invent.c b/src/invent.c index a9ff10a42..cb1cfb2d0 100644 --- a/src/invent.c +++ b/src/invent.c @@ -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 diff --git a/src/pickup.c b/src/pickup.c index 535bcb4bc..2395c4110 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -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,