From feac8c8f68ec24e5ed9ecffaae6ac29a600fc4d2 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 13 Apr 2022 03:14:39 -0700 Subject: [PATCH] 'm' prefix for drinking and dipping Allow the player to precede q/#quaff or M-d/#dip with the 'm' prefix to skip asking about fountains, sinks, or pools if one of those happens to be present, similar to how using it for e/#eat skips food on the floor and goes straight to inventory. If you use it and don't have any potions, you'll get "you don't have anything to drink" or "you don't have anything to dip into", same as when there is no suitable dungeon feature present combined with no potions. However, if an applicable dungeon feature is present and you don't use the prefix but answer 'no' to drink from fountain,&c and you don't have any potions, "else" will be inserted into the message: "you don't have anything else to drink". A big part of the diff is just a change in indentation level for code that is now inside 'if (!iflags.menu_requested) {' ... '}'. --- doc/Guidebook.mn | 16 +++- doc/Guidebook.tex | 22 +++++- doc/fixes3-7-0.txt | 2 + include/hack.h | 6 +- src/cmd.c | 4 +- src/invent.c | 7 ++ src/potion.c | 184 +++++++++++++++++++++++++++++---------------- 7 files changed, 168 insertions(+), 73 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 053d205b7..ee8640dec 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -651,7 +651,8 @@ a menu of several sorting alternatives (which sets a new value for the .op sortdiscoveries option). .lp "" -A few other commands (eat food, offer sacrifice, apply tinning-kit) use +A few other commands (eat food, offer sacrifice, apply tinning-kit, +drink/quaff, dip) use the \(oq\f(CRm\fP\(cq prefix to skip checking for applicable objects on the floor and go straight to checking inventory, or (for \(lq#loot\(rq to remove a saddle), @@ -878,6 +879,13 @@ For some interfaces, the behavior can be varied via the option. .lp q Quaff (drink) something (potion, water, etc). +.lp "" +When there is a fountain or sink present, it asks whether to drink +from that. +If that is declined, then it offers a chance to choose a potion from +inventory. +Precede \(oqq\(cq with the \(oqm\(cq prefix to skip asking about +drinking from a fountain or sink. .lp Q Select an object for your quiver, quiver sack, or just generally at the ready (only one of these is available at a time). @@ -1216,6 +1224,9 @@ See the section below entitled \(lqConduct\(rq for details. Dip an object into something. Autocompletes. Default key is \(oqM-d\(cq. +.lp "" +The \(oqm\(cq prefix skips dipping into a fountain or pool if there +is one at your location. .lp "#down " Go down a staircase. Default key is \(oq>\(cq. @@ -1419,6 +1430,9 @@ Default key is \(oqP\(cq. .lp "#quaff " Quaff (drink) something. Default key is \(oqq\(cq. +.lp "" +The \(oqm\(cq prefix skips drinking from a fountain or sink if there +is one at your location. .lp "#quit " Quit the program without saving your game. Autocompletes. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 90a993dba..8f7ad1bde 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -744,7 +744,8 @@ a menu of several sorting alternatives (which sets a new value for the {\it sortdiscoveries\/} option). \\ %.lp "" -A few other commands (eat food, offer sacrifice, apply tinning-kit) use +A few other commands (eat food, offer sacrifice, apply tinning-kit, +drink/quaff, dip) use the `{\tt m}' prefix to skip checking for applicable objects on the floor and go straight to checking inventory, or (for ``{\tt \#loot}'' to remove a saddle), @@ -972,7 +973,14 @@ For some interfaces, the behavior can be varied via the {\it msg\verb+_+window\/} option. %.lp \item[\tb{q}] -Quaff (drink) something (potion, water, etc). +Quaff (drink) something (potion, water, etc).\\ +%.lp "" +When there is a fountain or sink present, it asks whether to drink +from that. +If that is declined, then it offers a chance to choose a potion from +inventory. +Precede {\tt q} with the {\tt m} prefix to skip asking about +drinking from a fountain or sink. %.lp \item[\tb{Q}] Select an object for your quiver, quiver sack, or just generally at @@ -1317,7 +1325,10 @@ Default key is `{\tt M-C}'.\\ See the section below entitled ``Conduct'' for details. %.lp \item[\tb{\#dip}] -Dip an object into something. Autocompletes. Default key is `{\tt M-d}'. +Dip an object into something. Autocompletes. Default key is `{\tt M-d}'.\\ +%.lp "" +The {\tt m} prefix skips dipping into a fountain or pool if there +is one at your location. %.lp \item[\tb{\#down}] Go down a staircase. Default key is `{\tt >}'. @@ -1525,7 +1536,10 @@ Show previously displayed game messages. Default key is `{\tt \^{}P}'. Put on an accessory (ring, amulet, etc). Default key is `{\tt P}'. %.lp \item[\tb{\#quaff}] -Quaff (drink) something. Default key is `{\tt q}'. +Quaff (drink) something. Default key is `{\tt q}'.\\ +%.lp "" +The {\tt m} prefix skips drinking from a fountain or sink if there +is one at your location. %.lp \item[\tb{\#quit}] Quit the program without saving your game. Autocompletes.\\ diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 82a419754..f543c78e6 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -880,6 +880,8 @@ don't try to catch up for lost time for shop damage repair in restdamage() putting objects into a container with menustyle=traditional and then taking them back out with #tip would result in complaints about obj bypass bit being set if sanity_check was On +when drinking or dipping, allow the 'm' prefix to be used to skip asking + about fountains and pools Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/hack.h b/include/hack.h index 38c436e90..95edc50ee 100644 --- a/include/hack.h +++ b/include/hack.h @@ -583,7 +583,11 @@ enum getobj_callback_returns { /* generally invalid - can't be used for this purpose. will give a "silly * thing" message if the player tries to pick it, unless a more specific * failure message is in getobj itself - e.g. "You cannot foo gold". */ - GETOBJ_EXCLUDE = -2, + GETOBJ_EXCLUDE = -3, + /* invalid because it is not in inventory; used when the hands/self + * possibility is queried and the player passed up something on the + * floor before getobj. */ + GETOBJ_EXCLUDE_NONINVENT = -2, /* invalid because it is an inaccessible or unwanted piece of gear, but * psuedo-valid for the purposes of allowing the player to select it and * getobj to return it if there is a prompt instead of getting "silly diff --git a/src/cmd.c b/src/cmd.c index efe3288c4..b4b606482 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2244,7 +2244,7 @@ struct ext_func_tab extcmdlist[] = { { M('C'), "conduct", "list voluntary challenges you have maintained", doconduct, IFBURIED | AUTOCOMPLETE | GENERALCMD, NULL }, { M('d'), "dip", "dip an object into something", - dodip, AUTOCOMPLETE, NULL }, + dodip, AUTOCOMPLETE | CMD_M_PREFIX, NULL }, { '>', "down", "go down a staircase", /* allows 'm' prefix (for move without autopickup) but not the g/G/F movement modifiers; not flagged as MOVEMENTCMD because @@ -2336,7 +2336,7 @@ struct ext_func_tab extcmdlist[] = { { 'P', "puton", "put on an accessory (ring, amulet, etc)", doputon, 0, NULL }, { 'q', "quaff", "quaff (drink) something", - dodrink, 0, NULL }, + dodrink, CMD_M_PREFIX, NULL }, { '\0', "quit", "exit without saving current game", done2, IFBURIED | AUTOCOMPLETE | GENERALCMD | NOFUZZERCMD, NULL }, diff --git a/src/invent.c b/src/invent.c index d622ae4df..70a101a79 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1552,6 +1552,12 @@ getobj(const char *word, allownone = TRUE; *ap++ = HANDS_SYM; break; + case GETOBJ_EXCLUDE_NONINVENT: /* player skipped some alternative that's + * not in inventory, now the hands/self + * possibility is telling us so */ + forceprompt = FALSE; + inaccess++; + break; default: break; } @@ -1595,6 +1601,7 @@ getobj(const char *word, break; case GETOBJ_SUGGEST: break; /* adding otmp->invlet is all that's needed */ + case GETOBJ_EXCLUDE_NONINVENT: /* not applicable for invent items */ default: impossible("bad return from getobj callback"); } diff --git a/src/potion.c b/src/potion.c index 90ecd6263..fc125ccc7 100644 --- a/src/potion.c +++ b/src/potion.c @@ -42,6 +42,11 @@ static void hold_potion(struct obj *, const char *, const char *, const char *); static int potion_dip(struct obj *obj, struct obj *potion); +/* note: (*potn_test)() is for use by drink_ok() which is used to validate + potion to drink and also for potion to dip into [reinitialized every time + it's used so does not need to be placed in struct instance_globals g] */ +static boolean (*potn_test)(void) = (boolean (*)(void)) 0; + /* force `val' to be within valid range for intrinsic timeout value */ static long itimeout(long val) @@ -489,6 +494,27 @@ ghost_from_bottle(void) g.nomovemsg = "You regain your composure."; } +/* for drink_ok() when called for dodrink(); called thru (*potn_test) */ +static boolean +could_have_drunk(void) +{ + /* caveat: relies on knowing details of dodrink() */ + return (((IS_FOUNTAIN(levl[u.ux][u.uy].typ) + || IS_SINK(levl[u.ux][u.uy].typ)) + && can_reach_floor(FALSE)) + || (Underwater && !u.uswallow)); +} + +/* for drink_ok() when called for dodip(); called thru (*potn_test) */ +static boolean +could_have_dipped(void) +{ + /* caveat: relies on knowing details of dodip() */ + return ((IS_FOUNTAIN(levl[u.ux][u.uy].typ) + || is_pool(u.ux, u.uy)) + && can_reach_floor(FALSE)); +} + /* getobj callback for object to drink from, which also does double duty as the callback for dipping into (both just allow potions). */ static int @@ -497,6 +523,15 @@ drink_ok(struct obj *obj) if (obj && obj->oclass == POTION_CLASS) return GETOBJ_SUGGEST; + /* getobj()'s callback to test whether hands/self is a valid "item" to + pick is used here to communicate the fact that player has already + passed up an opportunity to perform the action (drink or dip) on a + non-inventory dungeon feature, so if there are no potions in invent + the message will be "you have nothing /else/ to {drink | dip into}"; + skip "else" if player used 'm' prefix to bypass dungeon features */ + if (!obj && !iflags.menu_requested && potn_test && (*potn_test)()) + return GETOBJ_EXCLUDE_NONINVENT; + return GETOBJ_EXCLUDE; } @@ -505,39 +540,47 @@ drink_ok(struct obj *obj) int dodrink(void) { - register struct obj *otmp; + struct obj *otmp; if (Strangled) { pline("If you can't breathe air, how can you drink liquid?"); return ECMD_OK; } - /* Is there a fountain to drink from here? */ - if (IS_FOUNTAIN(levl[u.ux][u.uy].typ) - /* not as low as floor level but similar restrictions apply */ - && can_reach_floor(FALSE)) { - if (yn("Drink from the fountain?") == 'y') { - drinkfountain(); - return ECMD_TIME; + + /* preceding 'q'/#quaff with 'm' skips the possibility of drinking + from fountains, sinks, and surrounding water plus the prompting + which those entail */ + if (!iflags.menu_requested) { + /* Is there a fountain to drink from here? */ + if (IS_FOUNTAIN(levl[u.ux][u.uy].typ) + /* not as low as floor level but similar restrictions apply */ + && can_reach_floor(FALSE)) { + if (yn("Drink from the fountain?") == 'y') { + drinkfountain(); + return ECMD_TIME; + } } - } - /* Or a kitchen sink? */ - if (IS_SINK(levl[u.ux][u.uy].typ) - /* not as low as floor level but similar restrictions apply */ - && can_reach_floor(FALSE)) { - if (yn("Drink from the sink?") == 'y') { - drinksink(); - return ECMD_TIME; + /* Or a kitchen sink? */ + if (IS_SINK(levl[u.ux][u.uy].typ) + /* not as low as floor level but similar restrictions apply */ + && can_reach_floor(FALSE)) { + if (yn("Drink from the sink?") == 'y') { + drinksink(); + return ECMD_TIME; + } } - } - /* Or are you surrounded by water? */ - if (Underwater && !u.uswallow) { - if (yn("Drink the water around you?") == 'y') { - pline("Do you know what lives in this water?"); - return ECMD_TIME; + /* Or are you surrounded by water? */ + if (Underwater && !u.uswallow) { + if (yn("Drink the water around you?") == 'y') { + pline("Do you know what lives in this water?"); + return ECMD_TIME; + } } } + potn_test = could_have_drunk; otmp = getobj("drink", drink_ok, GETOBJ_NOFLAGS); + potn_test = (boolean (*)(void)) 0; if (!otmp) return ECMD_CANCEL; @@ -2186,66 +2229,74 @@ dodip(void) if (inaccessible_equipment(obj, "dip", FALSE)) return ECMD_OK; - shortestname = (is_plural(obj) || pair_of(obj)) ? "them" : "it"; - /* - * Bypass safe_qbuf() since it doesn't handle varying suffix without - * an awful lot of support work. Format the object once, even though - * the fountain and pool prompts offer a lot more room for it. - * 3.6.0 used thesimpleoname() unconditionally, which posed no risk - * of buffer overflow but drew bug reports because it omits user- - * supplied type name. - * getobj: "What do you want to dip into? [xyz or ?*] " - */ - Strcpy(obuf, short_oname(obj, doname, thesimpleoname, + /* preceding #dip with 'm' skips the possibility of dipping into + fountains and pools plus the prompting which those entail */ + if (!iflags.menu_requested) { + shortestname = (is_plural(obj) || pair_of(obj)) ? "them" : "it"; + /* + * Bypass safe_qbuf() since it doesn't handle varying suffix without + * an awful lot of support work. Format the object once, even though + * the fountain and pool prompts offer a lot more room for it. + * 3.6.0 used thesimpleoname() unconditionally, which posed no risk + * of buffer overflow but drew bug reports because it omits user- + * supplied type name. + * getobj: "What do you want to dip into? [xyz or ?*] " + */ + Strcpy(obuf, short_oname(obj, doname, thesimpleoname, /* 128 - (24 + 54 + 1) leaves 49 for */ - QBUFSZ - sizeof "What do you want to dip \ + QBUFSZ - sizeof "What do you want to dip \ into? [abdeghjkmnpqstvwyzBCEFHIKLNOQRTUWXZ#-# or ?*] ")); - here = levl[u.ux][u.uy].typ; - /* Is there a fountain to dip into here? */ - if (IS_FOUNTAIN(here)) { - Snprintf(qbuf, sizeof(qbuf), "%s%s into the fountain?", Dip_, - flags.verbose ? obuf : shortestname); - /* "Dip into the fountain?" */ - if (yn(qbuf) == 'y') { - obj->pickup_prev = 0; - dipfountain(obj); - return ECMD_TIME; - } - } else if (is_pool(u.ux, u.uy)) { - const char *pooltype = waterbody_name(u.ux, u.uy); - - Snprintf(qbuf, sizeof(qbuf), "%s%s into the %s?", Dip_, - flags.verbose ? obuf : shortestname, pooltype); - /* "Dip into the {pool, moat, &c}?" */ - if (yn(qbuf) == 'y') { - if (Levitation) { - floating_above(pooltype); - } else if (u.usteed && !is_swimmer(u.usteed->data) - && P_SKILL(P_RIDING) < P_BASIC) { - rider_cant_reach(); /* not skilled enough to reach */ - } else { + here = levl[u.ux][u.uy].typ; + /* Is there a fountain to dip into here? */ + if (!can_reach_floor(FALSE)) { + ; /* can't dip something into fountain or pool if can't reach */ + } else if (IS_FOUNTAIN(here)) { + Snprintf(qbuf, sizeof(qbuf), "%s%s into the fountain?", Dip_, + flags.verbose ? obuf : shortestname); + /* "Dip into the fountain?" */ + if (yn(qbuf) == 'y') { obj->pickup_prev = 0; - if (obj->otyp == POT_ACID) - obj->in_use = 1; - if (water_damage(obj, 0, TRUE) != ER_DESTROYED && obj->in_use) - useup(obj); + dipfountain(obj); + return ECMD_TIME; + } + } else if (is_pool(u.ux, u.uy)) { + const char *pooltype = waterbody_name(u.ux, u.uy); + + Snprintf(qbuf, sizeof(qbuf), "%s%s into the %s?", Dip_, + flags.verbose ? obuf : shortestname, pooltype); + /* "Dip into the {pool, moat, &c}?" */ + if (yn(qbuf) == 'y') { + if (Levitation) { + floating_above(pooltype); + } else if (u.usteed && !is_swimmer(u.usteed->data) + && P_SKILL(P_RIDING) < P_BASIC) { + rider_cant_reach(); /* not skilled enough to reach */ + } else { + obj->pickup_prev = 0; + if (obj->otyp == POT_ACID) + obj->in_use = 1; + if (water_damage(obj, 0, TRUE) != ER_DESTROYED + && obj->in_use) + useup(obj); + } + return ECMD_TIME; } - return ECMD_TIME; } } - + potn_test = could_have_dipped; /* augment drink_ok() */ /* "What do you want to dip into? [xyz or ?*] " */ Snprintf(qbuf, sizeof qbuf, "dip %s into", flags.verbose ? obuf : shortestname); potion = getobj(qbuf, drink_ok, GETOBJ_NOFLAGS); + potn_test = (boolean (*)(void)) 0; if (!potion) return ECMD_CANCEL; return potion_dip(obj, potion); } /* #altdip - for context-sensitive inventory item-action; - potion already selected */ + potion already selected and pending in cmdq */ int dip_into(void) { @@ -2254,6 +2305,9 @@ dip_into(void) if (!cmdq_peek()) panic("dip_into: where is potion?"); + potn_test = (boolean (*)(void)) 0; /* not needed for this drink_ok() */ + /* note: drink_ok() callback for quaffing is also used to validate + a potion to dip into */ potion = getobj("dip", drink_ok, GETOBJ_NOFLAGS); if (!potion || potion->oclass != POTION_CLASS) return ECMD_CANCEL; -- 2.50.1