From 6106a7240fdf9dd3cbdfda54469ddb30442b7c66 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 7 Mar 2016 16:38:05 -0800 Subject: [PATCH] fix bz238 - looting many containers "Looting many containers via menu cannot be stopped". When the player uses #loot command at a location with multiple containers, a menu of which ones to loot is presented and player can pick any or all of them. But if you terminate the looting of a particular container with ESC, it goes on to the next selected one rather than stopping the loot action because that's what the 'q' choice does. The simplest fix would be to allow choosing only one container from the "loot which?" menu, but this retains the ability to loot multiple containers on a pile in one turn. It makes looting stoppable by extending the ":iobrsq or ?" prompt, adding 'n' for "next container" and changing 'q' from "done with this container" to "done looting" (with ESC still a synonym for 'q'). When just one container is being looted, or when on the last of N containers, 'n' is not shown but is still accepted (and treated as 'q'). Also, use_container() was using a menu for ":iobrsq" if player had menustyle set to Full when it was intended to be for Partial (name confusion...). This switches Partial to use menu for loot action, and leaves Full with that since that's how 3.6.0 has been behaving. Traditional and Combination use the prompt string and single char response. --- doc/fixes36.1 | 3 ++ include/extern.h | 4 +- src/apply.c | 4 +- src/pickup.c | 120 +++++++++++++++++++++++++++++++---------------- 4 files changed, 87 insertions(+), 44 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index d382342ee..c2f614219 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -177,6 +177,9 @@ a few types of monster (barrow wight, Nazgul, erinys) have weapon attacks that cockatrice corpse wouldn't be inflicted non-item-using monsters who happened to be carrying scroll or wand of fire or a fire horn could use it to cure themselves of being turned into slime +when looting multiple containers, add 'n' for "loot next container", change + 'q' and ESC from "done with this container" to "done looting" +change looting to choose ":iobrs nq" action from a menu for menustyle:Partial Platform- and/or Interface-Specific Fixes diff --git a/include/extern.h b/include/extern.h index 4277d3b63..a0b47449a 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1456992437 2016/03/03 08:07:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.550 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1457397476 2016/03/08 00:37:56 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.551 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1763,7 +1763,7 @@ E int NDECL(encumber_msg); E int NDECL(doloot); E boolean FDECL(container_gone, (int (*)(OBJ_P))); E boolean NDECL(u_handsy); -E int FDECL(use_container, (struct obj **, int)); +E int FDECL(use_container, (struct obj **, int, BOOLEAN_P)); E int FDECL(loot_mon, (struct monst *, int *, boolean *)); E int NDECL(dotip); E boolean FDECL(is_autopickup_exception, (struct obj *, BOOLEAN_P)); diff --git a/src/apply.c b/src/apply.c index e30542dd2..ddd253aee 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 apply.c $NHDT-Date: 1457207021 2016/03/05 19:43:41 $ $NHDT-Branch: chasonr $:$NHDT-Revision: 1.223 $ */ +/* NetHack 3.6 apply.c $NHDT-Date: 1457397477 2016/03/08 00:37:57 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.224 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -3430,7 +3430,7 @@ doapply() case SACK: case BAG_OF_HOLDING: case OILSKIN_SACK: - res = use_container(&obj, 1); + res = use_container(&obj, 1, FALSE); break; case BAG_OF_TRICKS: (void) bagotricks(obj, FALSE, (int *) 0); diff --git a/src/pickup.c b/src/pickup.c index fe738fa26..7a5deef0d 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pickup.c $NHDT-Date: 1453591408 2016/01/23 23:23:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.169 $ */ +/* NetHack 3.6 pickup.c $NHDT-Date: 1457397478 2016/03/08 00:37:58 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.170 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -33,16 +33,16 @@ STATIC_PTR int FDECL(out_container, (struct obj *)); STATIC_DCL void FDECL(removed_from_icebox, (struct obj *)); STATIC_DCL long FDECL(mbag_item_gone, (int, struct obj *)); STATIC_DCL void FDECL(observe_quantum_cat, (struct obj *)); -STATIC_DCL void NDECL(explain_container_prompt); +STATIC_DCL void FDECL(explain_container_prompt, (BOOLEAN_P)); STATIC_DCL int FDECL(traditional_loot, (BOOLEAN_P)); STATIC_DCL int FDECL(menu_loot, (int, BOOLEAN_P)); STATIC_DCL char FDECL(in_or_out_menu, (const char *, struct obj *, BOOLEAN_P, - BOOLEAN_P, BOOLEAN_P)); + BOOLEAN_P, BOOLEAN_P, BOOLEAN_P)); STATIC_DCL int FDECL(container_at, (int, int, BOOLEAN_P)); STATIC_DCL boolean FDECL(able_to_loot, (int, int, BOOLEAN_P)); STATIC_DCL boolean NDECL(reverse_loot); STATIC_DCL boolean FDECL(mon_beside, (int, int)); -STATIC_DCL int FDECL(do_loot_cont, (struct obj **)); +STATIC_DCL int FDECL(do_loot_cont, (struct obj **, int, int)); STATIC_DCL void FDECL(tipcontainer, (struct obj *)); /* define for query_objlist() and autopickup() */ @@ -66,6 +66,7 @@ STATIC_DCL void FDECL(tipcontainer, (struct obj *)); /* in_container() and out_container() from askchain() and use_container(). */ /* Also used by menu_loot() and container_gone(). */ static NEARDATA struct obj *current_container; +static NEARDATA boolean abort_looting; #define Icebox (current_container->otyp == ICE_BOX) static const char @@ -1579,16 +1580,22 @@ int x, y; } int -do_loot_cont(cobjp) +do_loot_cont(cobjp, cindex, ccount) struct obj **cobjp; +int cindex, ccount; /* index of this container (1..N), number of them (N) */ { struct obj *cobj = *cobjp; if (!cobj) return 0; if (cobj->olocked) { - pline("%s locked.", - cobj->lknown ? "It is" : "Hmmm, it turns out to be"); + if (ccount < 2) + pline("%s locked.", + cobj->lknown ? "It is" : "Hmmm, it turns out to be"); + else if (cobj->lknown) + pline("%s is locked.", The(xname(cobj))); + else + pline("Hmmm, %s turns out to be locked.", the(xname(cobj))); cobj->lknown = 1; return 0; } @@ -1597,17 +1604,18 @@ struct obj **cobjp; if (cobj->otyp == BAG_OF_TRICKS) { int tmp; - You("carefully open the bag..."); + You("carefully open %s...", the(xname(cobj))); pline("It develops a huge set of teeth and bites you!"); tmp = rnd(10); losehp(Maybe_Half_Phys(tmp), "carnivorous bag", KILLED_BY_AN); makeknown(BAG_OF_TRICKS); + abort_looting = TRUE; return 1; } You("%sopen %s...", (!cobj->cknown || !cobj->lknown) ? "carefully " : "", the(xname(cobj))); - return use_container(cobjp, 0); + return use_container(cobjp, 0, (boolean) (cindex < ccount)); } /* loot a container on the floor or loot saddle from mon. */ @@ -1626,6 +1634,8 @@ doloot() boolean prev_loot = FALSE; int num_conts; + abort_looting = FALSE; + if (check_capacity((char *) 0)) { /* "Can't do that while carrying so much stuff." */ return 0; @@ -1676,16 +1686,17 @@ lootcont: destroy_nhwindow(win); if (n > 0) { - for (i = 0; i < n; i++) { - timepassed |= do_loot_cont(&pick_list[i].item.a_obj); - if (multi < 0 || !pick_list[i].item.a_obj) { + for (i = 1; i <= n; i++) { + cobj = pick_list[i - 1].item.a_obj; + timepassed |= do_loot_cont(&cobj, i, n); + if (abort_looting) { + /* chest trap or magic bag explosion or */ free((genericptr_t) pick_list); - return 1; + return timepassed; } } - } - if (pick_list) free((genericptr_t) pick_list); + } if (n != 0) c = 'y'; } else { @@ -1702,11 +1713,10 @@ lootcont: continue; anyfound = TRUE; - timepassed |= do_loot_cont(&cobj); - /* might have triggered chest trap or magic bag explosion - */ - if (multi < 0 || !cobj) - return 1; + timepassed |= do_loot_cont(&cobj, 1, 1); + if (abort_looting) + /* chest trap or magic bag explosion or */ + return timepassed; } } if (anyfound) @@ -2234,14 +2244,16 @@ int FDECL((*fn), (OBJ_P)); } STATIC_OVL void -explain_container_prompt() +explain_container_prompt(more_containers) +boolean more_containers; { static const char *const explaintext[] = { "Container actions:", "", " : -- Look: examine contents", " o -- Out: take things out", " i -- In: put things in", " b -- Both: first take things out, then put things in", " r -- Reversed: put things in, then take things out", - " s -- Stash: put one item in", " q -- Quit: do nothing", + " s -- Stash: put one item in", "", + " n -- Next: loot next selected container", " q -- Quit: finished", " ? -- Help: display this text.", "", 0 }; const char *const *txtpp; @@ -2249,8 +2261,11 @@ explain_container_prompt() /* "Do what with ? [:oibrsq or ?] (q)" */ if ((win = create_nhwindow(NHW_TEXT)) != WIN_ERR) { - for (txtpp = explaintext; *txtpp; ++txtpp) + for (txtpp = explaintext; *txtpp; ++txtpp) { + if (!more_containers && !strncmp(*txtpp, " n ", 3)) + continue; putstr(win, 0, *txtpp); + } display_nhwindow(win, FALSE); destroy_nhwindow(win); } @@ -2272,9 +2287,10 @@ u_handsy() static const char stashable[] = { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, 0 }; int -use_container(objp, held) +use_container(objp, held, more_containers) struct obj **objp; int held; +boolean more_containers; /* True iff #loot multiple and this isn't last one */ { struct obj *curr, *otmp, *obj = *objp; boolean quantum_cat, cursed_mbag, loot_out, loot_in, loot_in_first, @@ -2282,6 +2298,7 @@ int held; char c, emptymsg[BUFSZ], qbuf[QBUFSZ], pbuf[QBUFSZ], xbuf[QBUFSZ]; int used = 0; + abort_looting = FALSE; emptymsg[0] = '\0'; if (!u_handsy()) @@ -2304,12 +2321,15 @@ int held; multi_reason = "opening a container"; nomovemsg = ""; } + abort_looting = TRUE; return 1; } obj->lknown = 1; current_container = obj; /* for use by in/out_container */ - /* from here on out, all early returns go through containerdone */ + /* + * From here on out, all early returns go through 'containerdone:'. + */ /* check for Schroedinger's Cat */ quantum_cat = SchroedingersBox(current_container); @@ -2375,14 +2395,15 @@ int held; (void) safe_qbuf(qbuf, "Do what with ", "?", current_container, yname, ysimple_name, "it"); /* ask player about what to do with this container */ - if (flags.menu_style == MENU_FULL) { + if (flags.menu_style == MENU_PARTIAL + || flags.menu_style == MENU_FULL) { if (!inokay && !outmaybe) { /* nothing to take out, nothing to put in; trying to do both will yield proper feedback */ c = 'b'; } else { c = in_or_out_menu(qbuf, current_container, outmaybe, inokay, - (used != 0)); + (boolean) (used != 0), more_containers); } } else { /* TRADITIONAL, COMBINATION, or PARTIAL */ xbuf[0] = '\0'; /* list of extra acceptable responses */ @@ -2391,18 +2412,22 @@ int held; Strcat(inokay ? pbuf : xbuf, "i"); /* put in */ Strcat(outmaybe ? pbuf : xbuf, "b"); /* both */ Strcat(inokay ? pbuf : xbuf, "rs"); /* reversed, stash */ + Strcat(pbuf, " "); + Strcat(more_containers ? pbuf : xbuf, "n"); Strcat(pbuf, "q"); /* quit */ if (iflags.cmdassist) + /* this unintentionally allows user to answer with 'o' or + 'r'; fortunately, those are already valid choices here */ Strcat(pbuf, " or ?"); /* help */ else Strcat(xbuf, "?"); if (*xbuf) Strcat(strcat(pbuf, "\033"), xbuf); - c = yn_function(qbuf, pbuf, 'q'); - } /* FULL vs other modes */ + c = yn_function(qbuf, pbuf, more_containers ? 'n' : 'q'); + } /* PARTIAL|FULL vs other modes */ if (c == '?') { - explain_container_prompt(); + explain_container_prompt(more_containers); } else if (c == ':') { /* note: will set obj->cknown */ if (!current_container->cknown) used = 1; /* gaining info */ @@ -2411,7 +2436,9 @@ int held; break; } /* loop until something other than '?' or ':' is picked */ - if (c == 'q') /* [not strictly needed; falling through works] */ + if (c == 'q') + abort_looting = TRUE; + if (c == 'n' || c == 'q') /* [not strictly needed; falling thru works] */ goto containerdone; loot_out = (c == 'o' || c == 'b' || c == 'r'); loot_in = (c == 'i' || c == 'b' || c == 'r'); @@ -2499,7 +2526,10 @@ containerdone: } *objp = current_container; /* might have become null */ - current_container = 0; /* avoid hanging on to stale pointer */ + if (current_container) + current_container = 0; /* avoid hanging on to stale pointer */ + else + abort_looting = TRUE; return used; } @@ -2623,13 +2653,13 @@ boolean put_in; } STATIC_OVL char -in_or_out_menu(prompt, obj, outokay, inokay, alreadyused) +in_or_out_menu(prompt, obj, outokay, inokay, alreadyused, more_containers) const char *prompt; struct obj *obj; -boolean outokay, inokay, alreadyused; +boolean outokay, inokay, alreadyused, more_containers; { /* underscore is not a choice; it's used to skip element [0] */ - static const char lootchars[] = "_:oibrsq", abc_chars[] = "_:abcdeq"; + static const char lootchars[] = "_:oibrsnq", abc_chars[] = "_:abcdenq"; winid win; anything any; menu_item *pick_list; @@ -2674,20 +2704,30 @@ boolean outokay, inokay, alreadyused; add_menu(win, NO_GLYPH, &any, menuselector[any.a_int], 0, ATR_NONE, buf, MENU_UNSELECTED); } - any.a_int = 7; /* 'q' */ + any.a_int = 0; + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + if (more_containers) { + any.a_int = 7; /* 'n' */ + add_menu(win, NO_GLYPH, &any, menuselector[any.a_int], 0, ATR_NONE, + "loot next container", MENU_SELECTED); + } + any.a_int = 8; /* 'q' */ Strcpy(buf, alreadyused ? "done" : "do nothing"); add_menu(win, NO_GLYPH, &any, menuselector[any.a_int], 0, ATR_NONE, buf, - MENU_SELECTED); + more_containers ? MENU_UNSELECTED : MENU_SELECTED); end_menu(win, prompt); n = select_menu(win, PICK_ONE, &pick_list); destroy_nhwindow(win); if (n > 0) { - n = pick_list[0].item.a_int; + int k = pick_list[0].item.a_int; + + if (n > 1 && k == (more_containers ? 7 : 8)) + k = pick_list[1].item.a_int; free((genericptr_t) pick_list); - return lootchars[n]; /* :,o,i,b,r,s,q */ + return lootchars[k]; /* :,o,i,b,r,s,n,q */ } - return 'q'; /* quit */ + return (n == 0 && more_containers) ? 'n' : 'q'; /* next or quit */ } static const char tippables[] = { ALL_CLASSES, TOOL_CLASS, 0 }; -- 2.40.0