]> granicus.if.org Git - nethack/commitdiff
fix #H6104 - no potion handling in thitu()
authorPatR <rankin@nethack.org>
Mon, 25 Sep 2017 17:42:43 +0000 (10:42 -0700)
committerPatR <rankin@nethack.org>
Mon, 25 Sep 2017 17:42:43 +0000 (10:42 -0700)
thitu() is mostly used for arrows and darts "thrown" by traps, but
scatter() uses it on items launched by a land mine explosion.  Traps
had no need for potion handling, but scattering does.  Changing thitu()
to call potionhit() required that more information be passed to the
latter in case killer reason was needed, and thitu()'s callers needed
to be updated since it now might use up its missile (only when that's
a potion, so scatter() is only caller which actually needed to care).

Quite a bit of work--especially the testing--for something which will
never be noticed in actual play.  In hindsight, it would have been
much simpler just to make scatter destroy all potions rather than
allow the 1% chance of remaining intact (via obj_resists()), or else
leave any intact ones at the explosion spot instead of launching them.

doc/fixes36.1
include/extern.h
include/obj.h
src/apply.c
src/dothrow.c
src/explode.c
src/mthrowu.c
src/potion.c
src/trap.c
src/uhitm.c
src/zap.c

index b7b8ddc33aee46eaf80ea770c7ef3bd0e154e693..c73d215dd3bfeb7b3c27850001fcc29b3381c90c 100644 (file)
@@ -446,6 +446,8 @@ fix buffer overflow in wizard mode for '#' command when 'extmenu' option is on
 fix mention_walls reporting secret doors as solid walls
 corpses and other flammable items not subject to direct burning or fire-based
        erosion which were thrown or dropped into lava remained intact
+if a potion on the floor survived a land mine explosion and got propelled at
+       the hero, it didn't behave like a potion if it hit
 
 
 Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository
index f4581f2e25d8e30b86e980857def1d01629e38a1..d571acac55f953eefd246486b22119a2842557df 100644 (file)
@@ -1533,7 +1533,7 @@ E void FDECL(Delay, (int));
 
 /* ### mthrowu.c ### */
 
-E int FDECL(thitu, (int, int, struct obj *, const char *));
+E int FDECL(thitu, (int, int, struct obj **, const char *));
 E int FDECL(ohitmon, (struct monst *, struct obj *, int, BOOLEAN_P));
 E void FDECL(thrwmu, (struct monst *));
 E int FDECL(spitmu, (struct monst *, struct attack *));
@@ -1883,7 +1883,7 @@ E int FDECL(dopotion, (struct obj *));
 E int FDECL(peffects, (struct obj *));
 E void FDECL(healup, (int, int, BOOLEAN_P, BOOLEAN_P));
 E void FDECL(strange_feeling, (struct obj *, const char *));
-E void FDECL(potionhit, (struct monst *, struct obj *, BOOLEAN_P));
+E void FDECL(potionhit, (struct monst *, struct obj *, int));
 E void FDECL(potionbreathe, (struct obj *));
 E int NDECL(dodip);
 E void FDECL(mongrantswish, (struct monst **));
index 824ccca39a1f65b7665befe6b32c62eb338f932c..4cf4c3d9eddd6cb562de7b010cda6c2f6f674cbc 100644 (file)
@@ -40,7 +40,7 @@ struct obj {
     unsigned owt;
     long quan; /* number of items */
 
-    schar spe; /* quality of weapon, armor or ring (+ or -);
+    schar spe; /* quality of weapon, weptool, armor or ring (+ or -);
                   number of charges for wand or charged tool ( >= -1 );
                   marks your eggs, tin variety and spinach tins;
                   Schroedinger's Box (1) or royal coffers for a court (2);
@@ -373,6 +373,12 @@ struct obj {
 #define ER_DAMAGED 2   /* object was damaged in some way */
 #define ER_DESTROYED 3 /* object was destroyed */
 
+/* propeller method for potionhit() */
+#define POTHIT_HERO_BASH   0 /* wielded by hero */
+#define POTHIT_HERO_THROW  1 /* thrown by hero */
+#define POTHIT_MONST_THROW 2 /* thrown by a monster */
+#define POTHIT_OTHER_THROW 3 /* propelled by some other means [scatter()] */
+
 /*
  *  Notes for adding new oextra structures:
  *
index dd50f32fa38c21ba9d12c5bbcd2f067324d49fd0..c72d65fbcfe49644e595559168d4acd04ac30f52 100644 (file)
@@ -2786,9 +2786,8 @@ struct obj *obj;
                         int hitu, hitvalu;
 
                         hitvalu = 8 + otmp->spe;
-                        hitu = thitu(hitvalu,
-                                     dmgval(otmp, &youmonst),
-                                     otmp, (char *)0);
+                        hitu = thitu(hitvalu, dmgval(otmp, &youmonst),
+                                     &otmp, (char *)0);
                         if (hitu) {
                             pline_The("%s hits you as you try to snatch it!",
                                       the(onambuf));
index a6825fcc9e2cd9117e45f1a089a34556f3596ec2..7194a028db8f1d6d3e3091c677314bed4ae6c575 100644 (file)
@@ -884,7 +884,7 @@ boolean hitsroof;
     /* object now hits you */
 
     if (obj->oclass == POTION_CLASS) {
-        potionhit(&youmonst, obj, TRUE);
+        potionhit(&youmonst, obj, POTHIT_HERO_THROW);
     } else if (breaktest(obj)) {
         int otyp = obj->otyp;
         int blindinc;
@@ -1086,7 +1086,7 @@ boolean
                    && rn2(6)) {
             /* alternative to prayer or wand of opening/spell of knock
                for dealing with cursed saddle:  throw holy water > */
-            potionhit(u.usteed, obj, TRUE);
+            potionhit(u.usteed, obj, POTHIT_HERO_THROW);
         } else {
             hitfloor(obj);
         }
@@ -1614,7 +1614,7 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */
 
     } else if (obj->oclass == POTION_CLASS
                && (guaranteed_hit || ACURR(A_DEX) > rnd(25))) {
-        potionhit(mon, obj, TRUE);
+        potionhit(mon, obj, POTHIT_HERO_THROW);
         return 1;
 
     } else if (befriend_with_obj(mon->data, obj)
index 50674f087e2f41da52e7308af0e1f6a90d69bfb6..643805dc40fb673c7fc1852d79432035aa31b73a 100644 (file)
@@ -570,7 +570,7 @@ struct obj *obj; /* only scatter this obj        */
     struct scatter_chain *schain = (struct scatter_chain *) 0;
     long total = 0L;
 
-    while ((otmp = individual_object ? obj : level.objects[sx][sy]) != 0) {
+    while ((otmp = (individual_object ? obj : level.objects[sx][sy])) != 0) {
         if (otmp->quan > 1L) {
             qtmp = otmp->quan - 1L;
             if (qtmp > LARGEST_INT)
@@ -584,8 +584,8 @@ struct obj *obj; /* only scatter this obj        */
         used_up = FALSE;
 
         /* 9 in 10 chance of fracturing boulders or statues */
-        if ((scflags & MAY_FRACTURE)
-            && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE))
+        if ((scflags & MAY_FRACTURE) != 0
+            && (otmp->otyp == BOULDER || otmp->otyp == STATUE)
             && rn2(10)) {
             if (otmp->otyp == BOULDER) {
                 if (cansee(sx, sy))
@@ -614,7 +614,7 @@ struct obj *obj; /* only scatter this obj        */
             used_up = TRUE;
 
             /* 1 in 10 chance of destruction of obj; glass, egg destruction */
-        } else if ((scflags & MAY_DESTROY)
+        } else if ((scflags & MAY_DESTROY) != 0
                    && (!rn2(10) || (objects[otmp->otyp].oc_material == GLASS
                                     || otmp->otyp == EGG))) {
             if (breaks(otmp, (xchar) sx, (xchar) sy))
@@ -622,8 +622,7 @@ struct obj *obj; /* only scatter this obj        */
         }
 
         if (!used_up) {
-            stmp = (struct scatter_chain *)
-                                         alloc(sizeof (struct scatter_chain));
+            stmp = (struct scatter_chain *) alloc(sizeof *stmp);
             stmp->next = (struct scatter_chain *) 0;
             stmp->obj = otmp;
             stmp->ox = sx;
@@ -679,7 +678,9 @@ struct obj *obj; /* only scatter this obj        */
                         if (bigmonst(youmonst.data))
                             hitvalu++;
                         hitu = thitu(hitvalu, dmgval(stmp->obj, &youmonst),
-                                     stmp->obj, (char *) 0);
+                                     &stmp->obj, (char *) 0);
+                        if (!stmp->obj)
+                            stmp->stopped = TRUE;
                         if (hitu) {
                             stmp->range -= 3;
                             stop_occupation();
index c36c0fe75a27dd352f7389ecf11d74d631088e48..3d2830b96aaea9fd8adf6ed9f24ae3d7000c1b90 100644 (file)
@@ -29,11 +29,12 @@ extern boolean notonhead; /* for long worms */
 
 /* hero is hit by something other than a monster */
 int
-thitu(tlev, dam, obj, name)
+thitu(tlev, dam, objp, name)
 int tlev, dam;
-struct obj *obj;
-const char *name; /* if null, then format `obj' */
+struct obj **objp;
+const char *name; /* if null, then format `*objp' */
 {
+    struct obj *obj = objp ? *objp : 0;
     const char *onm, *knm;
     boolean is_acid;
     int kprefix = KILLED_BY_AN;
@@ -42,8 +43,8 @@ const char *name; /* if null, then format `obj' */
     if (!name) {
         if (!obj)
             panic("thitu: name & obj both null?");
-        name =
-            strcpy(onmbuf, (obj->quan > 1L) ? doname(obj) : mshot_xname(obj));
+        name = strcpy(onmbuf,
+                      (obj->quan > 1L) ? doname(obj) : mshot_xname(obj));
         knm = strcpy(knmbuf, killer_xname(obj));
         kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */
     } else {
@@ -70,14 +71,20 @@ const char *name; /* if null, then format `obj' */
         else
             You("are hit by %s%s", onm, exclam(dam));
 
-        if (obj && objects[obj->otyp].oc_material == SILVER && Hate_silver) {
-            /* extra damage already applied by dmgval() */
-            pline_The("silver sears your flesh!");
-            exercise(A_CON, FALSE);
-        }
         if (is_acid && Acid_resistance) {
             pline("It doesn't seem to hurt you.");
+        } else if (obj && obj->oclass == POTION_CLASS) {
+            /* an explosion which scatters objects might hit hero with one
+               (potions deliberately thrown at hero are handled by m_throw) */
+            potionhit(&youmonst, obj, POTHIT_OTHER_THROW);
+            *objp = obj = 0; /* potionhit() uses up the potion */
         } else {
+            if (obj && objects[obj->otyp].oc_material == SILVER
+                && Hate_silver) {
+                /* extra damage already applied by dmgval() */
+                pline_The("silver sears your flesh!");
+                exercise(A_CON, FALSE);
+            }
             if (is_acid)
                 pline("It burns!");
             losehp(dam, knm, kprefix); /* acid damage */
@@ -110,9 +117,9 @@ int x, y;
     else
         create = 1;
 
-    if (create
-        && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) && (t = t_at(x, y))
-             && ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT)))) {
+    if (create && !((mtmp = m_at(x, y)) != 0 && mtmp->mtrapped
+                    && (t = t_at(x, y)) != 0
+                    && (t->ttyp == PIT || t->ttyp == SPIKED_PIT))) {
         int objgone = 0;
 
         if (down_gate(x, y) != -1)
@@ -329,7 +336,9 @@ boolean verbose;    /* give message(s) even when you can't see what happened */
         mtmp->msleeping = 0;
         if (vis)
             otmp->dknown = 1;
-        potionhit(mtmp, otmp, FALSE);
+        /* probably thrown by a monster rather than 'other', but the
+           distinction only matters when hitting the hero */
+        potionhit(mtmp, otmp, POTHIT_OTHER_THROW);
         return 1;
     } else {
         damage = dmgval(otmp, mtmp);
@@ -549,7 +558,7 @@ struct obj *obj;         /* missile (or stack providing it) */
             if (singleobj->oclass == POTION_CLASS) {
                 if (!Blind)
                     singleobj->dknown = 1;
-                potionhit(&youmonst, singleobj, FALSE);
+                potionhit(&youmonst, singleobj, POTHIT_MONST_THROW);
                 break;
             }
             oldumort = u.umortality;
@@ -565,7 +574,7 @@ struct obj *obj;         /* missile (or stack providing it) */
             /* fall through */
             case CREAM_PIE:
             case BLINDING_VENOM:
-                hitu = thitu(8, 0, singleobj, (char *) 0);
+                hitu = thitu(8, 0, &singleobj, (char *) 0);
                 break;
             default:
                 dam = dmgval(singleobj, &youmonst);
@@ -585,7 +594,7 @@ struct obj *obj;         /* missile (or stack providing it) */
                 hitv += 8 + singleobj->spe;
                 if (dam < 1)
                     dam = 1;
-                hitu = thitu(hitv, dam, singleobj, (char *) 0);
+                hitu = thitu(hitv, dam, &singleobj, (char *) 0);
             }
             if (hitu && singleobj->opoisoned && is_poisonable(singleobj)) {
                 char onmbuf[BUFSZ], knmbuf[BUFSZ];
@@ -888,7 +897,7 @@ struct monst *mtmp;
         if (dam < 1)
             dam = 1;
 
-        (void) thitu(hitv, dam, otmp, (char *) 0);
+        (void) thitu(hitv, dam, &otmp, (char *) 0);
         stop_occupation();
         return;
     }
index 1a7e08ecc5b4174abef45f7fe30962f5e1a3f769..cdaf9c1d3049f8ceecc55cc004099ecdb719a940 100644 (file)
@@ -1243,24 +1243,28 @@ const char *objphrase; /* "Your widget glows" or "Steed's saddle glows" */
     return res;
 }
 
+/* potion obj hits monster mon, which might be youmounst; obj always use up */
 void
-potionhit(mon, obj, your_fault)
-register struct monst *mon;
-register struct obj *obj;
-boolean your_fault;
+potionhit(mon, obj, how)
+struct monst *mon;
+struct obj *obj;
+int how;
 {
     const char *botlnam = bottlename();
     boolean isyou = (mon == &youmonst);
     int distance, tx, ty;
     struct obj *saddle = (struct obj *) 0;
-    boolean hit_saddle = FALSE;
+    boolean hit_saddle = FALSE, your_fault = (how <= POTHIT_HERO_THROW);
 
     if (isyou) {
         tx = u.ux, ty = u.uy;
         distance = 0;
         pline_The("%s crashes on your %s and breaks into shards.", botlnam,
                   body_part(HEAD));
-        losehp(Maybe_Half_Phys(rnd(2)), "thrown potion", KILLED_BY_AN);
+        losehp(Maybe_Half_Phys(rnd(2)),
+               (how == POTHIT_OTHER_THROW) ? "propelled potion" /* scatter */
+                                           : "thrown potion",
+               KILLED_BY_AN);
     } else {
         tx = mon->mx, ty = mon->my;
         /* sometimes it hits the saddle */
@@ -1314,6 +1318,7 @@ boolean your_fault;
         case POT_ACID:
             if (!Acid_resistance) {
                 int dmg;
+
                 pline("This burns%s!",
                       obj->blessed ? " a little"
                                    : obj->cursed ? " a lot" : "");
index d6b3f8de9ed49afdf84fc4eaf4f69a698f4814c6..91584da94981d39542bb3c9e52aa7af172bee787 100644 (file)
@@ -844,7 +844,7 @@ register struct trap *trap;
 unsigned trflags;
 {
     register int ttype = trap->ttyp;
-    register struct obj *otmp;
+    struct obj *otmp;
     boolean already_seen = trap->tseen,
             forcetrap = (trflags & FORCETRAP) != 0,
             webmsgok = (trflags & NOWEBMSG) == 0,
@@ -915,8 +915,9 @@ unsigned trflags;
         otmp->opoisoned = 0;
         if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) { /* nothing */
             ;
-        } else if (thitu(8, dmgval(otmp, &youmonst), otmp, "arrow")) {
-            obfree(otmp, (struct obj *) 0);
+        } else if (thitu(8, dmgval(otmp, &youmonst), &otmp, "arrow")) {
+            if (otmp)
+                obfree(otmp, (struct obj *) 0);
         } else {
             place_object(otmp, u.ux, u.uy);
             if (!Blind)
@@ -944,13 +945,15 @@ unsigned trflags;
         oldumort = u.umortality;
         if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) { /* nothing */
             ;
-        } else if (thitu(7, dmgval(otmp, &youmonst), otmp, "little dart")) {
-            if (otmp->opoisoned)
-                poisoned("dart", A_CON, "little dart",
-                         /* if damage triggered life-saving,
-                            poison is limited to attrib loss */
-                         (u.umortality > oldumort) ? 0 : 10, TRUE);
-            obfree(otmp, (struct obj *) 0);
+        } else if (thitu(7, dmgval(otmp, &youmonst), &otmp, "little dart")) {
+            if (otmp) {
+                if (otmp->opoisoned)
+                    poisoned("dart", A_CON, "little dart",
+                             /* if damage triggered life-saving,
+                                poison is limited to attrib loss */
+                             (u.umortality > oldumort) ? 0 : 10, TRUE);
+                obfree(otmp, (struct obj *) 0);
+            }
         } else {
             place_object(otmp, u.ux, u.uy);
             if (!Blind)
@@ -1802,7 +1805,7 @@ int style;
             if (multi)
                 nomul(0);
             if (thitu(9 + singleobj->spe, dmgval(singleobj, &youmonst),
-                      singleobj, (char *) 0))
+                      &singleobj, (char *) 0))
                 stop_occupation();
         }
         if (style == ROLL) {
index 81a71a4069d03b3f8c4fa016a2f9b4707004708a..8beb2aa4eceb61ae81b937256af4153c0d918102 100644 (file)
@@ -731,7 +731,8 @@ int thrown; /* HMON_xxx (0 => hand-to-hand, other => ranged) */
             else
                 setuwep((struct obj *) 0);
             freeinv(obj);
-            potionhit(mon, obj, TRUE);
+            potionhit(mon, obj,
+                      hand_to_hand ? POTHIT_HERO_BASH : POTHIT_HERO_THROW);
             if (mon->mhp <= 0)
                 return FALSE; /* killed */
             hittxt = TRUE;
index add78a0de0612b8e686cd8a9f2acaae7496ab805..6d82ab48e1af96f362a6b38e039038e022ab1ed5 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -3429,7 +3429,7 @@ int dx, dy;
         if (bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */
             if (Fumbling || rn2(20) >= ACURR(A_DEX)) {
                 /* we hit ourselves */
-                (void) thitu(10 + obj->spe, dmgval(obj, &youmonst), obj,
+                (void) thitu(10 + obj->spe, dmgval(obj, &youmonst), &obj,
                              "boomerang");
                 endmultishot(TRUE);
                 break;