From 6a9217d568fe4bbaf01b2b7512ae586381c00779 Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Sun, 19 Feb 2012 02:06:43 +0000 Subject: [PATCH] nerf potion alchemy (trunk only) Something I've had in mind for a long time: make it harder to acquire dozens of potions of full healing and thereby (8 * dozens) of extra hit points. When dipping a stack of more than 2 potions into another potion other than water, first split the stack so that 2 to 9 potions are dipped rather than all of them. Alchemy still works but it will take more dips (hence need more resources) to convert large amounts. Dipping into water isn't affected, nor is dipping non-potions. Also fix a bug where dipping a stack--now perhaps a subset stack after splitting--which triggered an explosion was only using up one of the dipped potions (plus the one being dipped into) instead of the whole stack. --- doc/fixes35.0 | 3 + src/potion.c | 148 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 93 insertions(+), 58 deletions(-) diff --git a/doc/fixes35.0 b/doc/fixes35.0 index 55fb6a337..a4e6b1bd2 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -409,6 +409,8 @@ theft of worn armor with wear/unwear delay would interfere with completion of (disrupted wear attempt for +N helm of brilliance would result in loss of N points of Int and Wis; gauntlets of dexterity had similar problem) #sit while swallowed would give the wrong message +alchemical explosion or evaporation only used up one potion instead of all + the potions being dipped Platform- and/or Interface-Specific Fixes @@ -558,6 +560,7 @@ display version and build information at startup repeatedly setting the fruit option will check to see if fruits have been created, so the user can't easily overflow the maximum this way bones files now include extra data to identify dead hero and reason for death +dipping multiple potions in another potion may only dip part of their stack Platform- and/or Interface-Specific New Features diff --git a/src/potion.c b/src/potion.c index 372554787..42124f288 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1815,14 +1815,14 @@ dodip() potion : obj, 5, 95)) { pline1(nothing_happens); } else { - boolean was_wep = FALSE, was_swapwep = FALSE, was_quiver = FALSE; + boolean was_wep, was_swapwep, was_quiver; short save_otyp = obj->otyp; /* KMH, conduct */ u.uconduct.polypiles++; - if (obj == uwep) was_wep = TRUE; - else if (obj == uswapwep) was_swapwep = TRUE; - else if (obj == uquiver) was_quiver = TRUE; + was_wep = (obj == uwep); + was_swapwep = (obj == uswapwep); + was_quiver = (obj == uquiver); obj = poly_obj(obj, STRANGE_OBJECT); @@ -1842,66 +1842,98 @@ dodip() } potion->in_use = FALSE; /* didn't go poof */ return(1); - } else if(obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) { - /* Mixing potions is dangerous... */ - pline_The("potions mix..."); - /* KMH, balance patch -- acid is particularly unstable */ - if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) { - pline("BOOM! They explode!"); - wake_nearto(u.ux, u.uy, (BOLT_LIM+1)*(BOLT_LIM+1)); - exercise(A_STR, FALSE); - if (!breathless(youmonst.data) || haseyes(youmonst.data)) - potionbreathe(obj); - useup(obj); - useup(potion); - losehp(rnd(10), "alchemic blast", /* not physical damage */ - KILLED_BY_AN); - return(1); + } else if (obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) { + long amt = obj->quan; + + Strcpy(qbuf, "The"); + if (amt > (objects[potion->otyp].oc_magic ? 2L : 9L)) { + /* trying to dip multiple potions will usually affect only a + subset; pick an amount between 2 and min(N,9), inclusive */ + amt -= 1L; + do { + amt = (long)rnd((int)amt); + } while (amt >= 9L); + amt += 1L; + if (amt < obj->quan) { + obj = splitobj(obj, amt); + Sprintf(qbuf, "%ld of the", obj->quan); } + } + /* [N of] the {obj(s)} mix(es) with [one of] {the potion}... */ + pline("%s %s %s with %s%s...", qbuf, + simpleonames(obj), otense(obj, "mix"), + (potion->quan > 1L) ? "one of " : "", + thesimpleoname(potion)); + /* Mixing potions is dangerous... + KMH, balance patch -- acid is particularly unstable */ + if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) { + /* it would be better to use up the whole stack in advance + of the message, but we can't because we need to keep it + around for potionbreathe() [and we can't set obj->in_use + to 'amt' because that's not implemented] */ + obj->in_use = 1; + pline("BOOM! They explode!"); + wake_nearto(u.ux, u.uy, (BOLT_LIM+1)*(BOLT_LIM+1)); + exercise(A_STR, FALSE); + if (!breathless(youmonst.data) || haseyes(youmonst.data)) + potionbreathe(obj); + useupall(obj); + useup(potion); + losehp((int)(amt + rnd(9)), /* not physical damage */ + "alchemic blast", KILLED_BY_AN); + return 1; + } - obj->blessed = obj->cursed = obj->bknown = 0; - if (Blind || Hallucination) obj->dknown = 0; - - if ((mixture = mixtype(obj, potion)) != 0) { - obj->otyp = mixture; - } else { - switch (obj->odiluted ? 1 : rnd(8)) { - case 1: - obj->otyp = POT_WATER; - break; - case 2: - case 3: - obj->otyp = POT_SICKNESS; - break; - case 4: - { - struct obj *otmp; - otmp = mkobj(POTION_CLASS,FALSE); - obj->otyp = otmp->otyp; - obfree(otmp, (struct obj *)0); - } - break; - default: - if (!Blind) - pline_The("mixture glows brightly and evaporates."); - useup(obj); - useup(potion); - return(1); - } - } + obj->blessed = obj->cursed = obj->bknown = 0; + if (Blind || Hallucination) obj->dknown = 0; - obj->odiluted = (obj->otyp != POT_WATER); + if ((mixture = mixtype(obj, potion)) != 0) { + obj->otyp = mixture; + } else { + switch (obj->odiluted ? 1 : rnd(8)) { + case 1: + obj->otyp = POT_WATER; + break; + case 2: + case 3: + obj->otyp = POT_SICKNESS; + break; + case 4: + { + struct obj *otmp = mkobj(POTION_CLASS, FALSE); - if (obj->otyp == POT_WATER && !Hallucination) { - pline_The("mixture bubbles%s.", - Blind ? "" : ", then clears"); - } else if (!Blind) { - pline_The("mixture looks %s.", - hcolor(OBJ_DESCR(objects[obj->otyp]))); + obj->otyp = otmp->otyp; + obfree(otmp, (struct obj *)0); + } + break; + default: + useupall(obj); + useup(potion); + if (!Blind) + pline_The("mixture glows brightly and evaporates."); + return 1; } + } + obj->odiluted = (obj->otyp != POT_WATER); - useup(potion); - return(1); + if (obj->otyp == POT_WATER && !Hallucination) { + pline_The("mixture bubbles%s.", Blind ? "" : ", then clears"); + } else if (!Blind) { + pline_The("mixture looks %s.", + hcolor(OBJ_DESCR(objects[obj->otyp]))); + } + + useup(potion); + /* this is required when 'obj' was split off from a bigger stack, + so that 'obj' will now be assigned its own inventory slot; + it has a side-effect of merging 'obj' into another compatible + stack if there is one, so we do it even when no split has + been made in order to get the merge result for both cases; + as a consequence, mixing while Fumbling drops the mixture */ + freeinv(obj); + (void) hold_another_object(obj, "You drop %s!", doname(obj), + (const char *)0); + return 1; } #ifdef INVISIBLE_OBJECTS -- 2.40.0