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;