From 719af503e763b316cc6642c7584fff8402dd5955 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 25 Sep 2017 10:42:43 -0700 Subject: [PATCH] fix #H6104 - no potion handling in thitu() 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 | 2 ++ include/extern.h | 4 ++-- include/obj.h | 8 +++++++- src/apply.c | 5 ++--- src/dothrow.c | 6 +++--- src/explode.c | 15 ++++++++------- src/mthrowu.c | 45 +++++++++++++++++++++++++++------------------ src/potion.c | 17 +++++++++++------ src/trap.c | 25 ++++++++++++++----------- src/uhitm.c | 3 ++- src/zap.c | 2 +- 11 files changed, 79 insertions(+), 53 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index b7b8ddc33..c73d215dd 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -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 diff --git a/include/extern.h b/include/extern.h index f4581f2e2..d571acac5 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 **)); diff --git a/include/obj.h b/include/obj.h index 824ccca39..4cf4c3d9e 100644 --- a/include/obj.h +++ b/include/obj.h @@ -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: * diff --git a/src/apply.c b/src/apply.c index dd50f32fa..c72d65fbc 100644 --- a/src/apply.c +++ b/src/apply.c @@ -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)); diff --git a/src/dothrow.c b/src/dothrow.c index a6825fcc9..7194a028d 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -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) diff --git a/src/explode.c b/src/explode.c index 50674f087..643805dc4 100644 --- a/src/explode.c +++ b/src/explode.c @@ -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(); diff --git a/src/mthrowu.c b/src/mthrowu.c index c36c0fe75..3d2830b96 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -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; } diff --git a/src/potion.c b/src/potion.c index 1a7e08ecc..cdaf9c1d3 100644 --- a/src/potion.c +++ b/src/potion.c @@ -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" : ""); diff --git a/src/trap.c b/src/trap.c index d6b3f8de9..91584da94 100644 --- a/src/trap.c +++ b/src/trap.c @@ -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) { diff --git a/src/uhitm.c b/src/uhitm.c index 81a71a406..8beb2aa4e 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -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; diff --git a/src/zap.c b/src/zap.c index add78a0de..6d82ab48e 100644 --- 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; -- 2.40.0