From 5af1fad3987b76c2b8caf1ec578ee1de57b444df Mon Sep 17 00:00:00 2001 From: Michael Meyer Date: Tue, 24 Oct 2023 18:02:30 -0400 Subject: [PATCH] Enable hero to wash hands in fountains and pools Dipping hands (with '-') or currently-worn gloves in a fountain or pool will cause the hero to wash her hands, washing away any oil and clearing the Glib intrinsic timeout. This does mean bare hands can be used to fish for fountain effects, without having to pick up a stray arrow and carry it around as a dipping item, but having your hands be the only thing that does not activate those effects felt weird. If that would be too unbalancing this could be scrapped. --- include/extern.h | 1 + src/fountain.c | 34 +++++++++++++++++++++++++++++----- src/potion.c | 36 ++++++++++++++++++++++++++++-------- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/include/extern.h b/include/extern.h index 65c4ba256..e2beed696 100644 --- a/include/extern.h +++ b/include/extern.h @@ -972,6 +972,7 @@ extern void dogushforth(int); extern void dryup(coordxy, coordxy, boolean); extern void drinkfountain(void); extern void dipfountain(struct obj *); +extern int wash_hands(void); extern void breaksink(coordxy, coordxy); extern void drinksink(void); diff --git a/src/fountain.c b/src/fountain.c index 60145d642..229078578 100644 --- a/src/fountain.c +++ b/src/fountain.c @@ -389,6 +389,7 @@ void dipfountain(register struct obj *obj) { int er = ER_NOTHING; + boolean is_hands = (obj == &cg.zeroobj); if (Levitation) { floating_above("fountain"); @@ -437,17 +438,19 @@ dipfountain(register struct obj *obj) if (in_town(u.ux, u.uy)) (void) angry_guards(FALSE); return; + } else if (is_hands || obj == uarmg) { + er = wash_hands(); } else { er = water_damage(obj, NULL, TRUE); + } - if (er == ER_DESTROYED || (er != ER_NOTHING && !rn2(2))) { - return; /* no further effect */ - } + if (er == ER_DESTROYED || (er != ER_NOTHING && !rn2(2))) { + return; /* no further effect */ } switch (rnd(30)) { case 16: /* Curse the item */ - if (obj->oclass != COIN_CLASS && !obj->cursed) { + if (!is_hands && obj->oclass != COIN_CLASS && !obj->cursed) { curse(obj); } break; @@ -455,7 +458,7 @@ dipfountain(register struct obj *obj) case 18: case 19: case 20: /* Uncurse the item */ - if (obj->cursed) { + if (!is_hands && obj->cursed) { if (!Blind) pline_The("%s glows for a moment.", hliquid("water")); uncurse(obj); @@ -539,6 +542,27 @@ dipfountain(register struct obj *obj) dryup(u.ux, u.uy, TRUE); } +int +wash_hands(void) +{ + const char *hands = makeplural(body_part(HAND)); + int res = ER_NOTHING; + + You("wash your %s%s in the %s.", uarmg ? "gloved " : "", hands, + hliquid("water")); + if (Glib) { + make_glib(0); + Your("%s are no longer slippery.", + uarmg ? gloves_simple_name(uarmg) : hands); + /* not what ER_GREASED is for, but the checks in dipfountain just + compare the result to ER_DESTROYED and ER_NOTHING, so it works */ + res = ER_GREASED; + } else if (uarmg) { + res = water_damage(uarmg, (const char *) 0, TRUE); + } + return res; +} + void breaksink(coordxy x, coordxy y) { diff --git a/src/potion.c b/src/potion.c index 0ae6e8db8..19b954312 100644 --- a/src/potion.c +++ b/src/potion.c @@ -2177,8 +2177,10 @@ mixtype(struct obj *o1, struct obj *o2) static int dip_ok(struct obj *obj) { - /* dipping hands and gold isn't currently implemented */ - if (!obj || obj->oclass == COIN_CLASS) + if (!obj) + return GETOBJ_DOWNPLAY; + /* dipping gold isn't currently implemented */ + if (obj->oclass == COIN_CLASS) return GETOBJ_EXCLUDE; if (inaccessible_equipment(obj, (const char *) 0, FALSE)) @@ -2218,14 +2220,16 @@ dodip(void) uchar here; char qbuf[QBUFSZ], obuf[QBUFSZ]; const char *shortestname; /* last resort obj name for prompt */ + boolean is_hands; if (!(obj = getobj("dip", dip_ok, GETOBJ_PROMPT))) return ECMD_CANCEL; if (inaccessible_equipment(obj, "dip", FALSE)) return ECMD_OK; - shortestname = (is_plural(obj) || pair_of(obj)) ? "them" : "it"; - + is_hands = (obj == &cg.zeroobj); + shortestname = (is_hands || is_plural(obj) || pair_of(obj)) ? "them" + : "it"; drink_ok_extra = 0; /* preceding #dip with 'm' skips the possibility of dipping into fountains and pools plus the prompting which those entail */ @@ -2239,10 +2243,16 @@ dodip(void) * 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 \ + if (is_hands) { + Snprintf(obuf, sizeof(obuf), "your %s", + makeplural(body_part(HAND))); + } else { + Strcpy(obuf, short_oname(obj, doname, thesimpleoname, + /* 128 - (24 + 54 + 1) leaves 49 for + */ + 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? */ @@ -2253,7 +2263,8 @@ dodip(void) flags.verbose ? obuf : shortestname); /* "Dip into the fountain?" */ if (y_n(qbuf) == 'y') { - obj->pickup_prev = 0; + if (!is_hands) + obj->pickup_prev = 0; dipfountain(obj); return ECMD_TIME; } @@ -2270,6 +2281,10 @@ dodip(void) } else if (u.usteed && !is_swimmer(u.usteed->data) && P_SKILL(P_RIDING) < P_BASIC) { rider_cant_reach(); /* not skilled enough to reach */ + } else if (is_hands || obj == uarmg) { + if (!is_hands) + obj->pickup_prev = 0; + (void) wash_hands(); } else { obj->pickup_prev = 0; if (obj->otyp == POT_ACID) @@ -2338,6 +2353,11 @@ potion_dip(struct obj *obj, struct obj *potion) pline("That is a potion bottle, not a Klein bottle!"); return ECMD_OK; } + if (obj == &cg.zeroobj) { + You("can't fit your %s into the mouth of the bottle!", + body_part(HAND)); + return ECMD_OK; + } obj->pickup_prev = 0; /* no longer 'recently picked up' */ potion->in_use = TRUE; /* assume it will be used up */