From b0478ecef85a550e0e34def1d3c2a7cae799eafc Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Mon, 20 Oct 2008 00:57:16 +0000 Subject: [PATCH] breaking wands (trunk only) From a bug report, polymorph of self due to breaking a wand also polymorphed various items that were dropped in the process, unlike the case of zapping polymorph at monsters which excludes dropped items from being poly'd. This polymorphs the pile at the hero's feet before polymorphing the hero. I first tried to handle it using obj->bypass like with monsters, but that didn't work. Post-3.4.3, the bypass handling is also used for polyself (by retouch_equipment()) and it was getting reset at an inconvenient time. He also complained that he failed to get "you feel shuddering vibrations" when some polymorphed objects got destroyed. That message is issued by weffects() which do_break_wand() doesn't call. It ought to be fixed, but this patch doesn't address it. Lastly, add code to prevent objects guarded against polymorph via obj->bypass from getting used up when creating polypile golems. --- doc/fixes35.0 | 3 +++ src/apply.c | 57 +++++++++++++++++++++++++++++++++++---------------- src/zap.c | 13 +++++++++++- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/doc/fixes35.0 b/doc/fixes35.0 index 2c6b4fd92..542ce3b44 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -287,6 +287,9 @@ when probing from inside an engulfer, "not carrying anything" overlooked hero wearing or removing an amulet of restful sleep clobbered permanent sleepiness if attempt to select a co-aligned artifact for first divine gift fails because none is available, choose one from among nonaligned artifacts +if breaking a wand of polymorph causes hero to drop items, don't transform them +if polymorph causes a monster to drop items, they won't be used up via + shuddering vibrations or as golem creation fodder Platform- and/or Interface-Specific Fixes diff --git a/src/apply.c b/src/apply.c index c97cd8558..9036c6ee8 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)apply.c 3.5 2008/03/07 */ +/* SCCS Id: @(#)apply.c 3.5 2008/10/14 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2891,6 +2891,7 @@ do_break_wand(obj) /* we want this before the explosion instead of at the very end */ pline("A wall of force smashes down around you!"); dmg = d(1 + obj->spe,6); /* normally 2d12 */ + /*FALLTHRU*/ case WAN_CANCELLATION: case WAN_POLYMORPH: case WAN_TELEPORTATION: @@ -2902,6 +2903,9 @@ do_break_wand(obj) } /* magical explosion and its visual effect occur before specific effects */ + /* [TODO? This really ought to prevent the explosion from being + fatal so that we never leave a bones file where none of the + surrounding targets (or underlying objects) got affected yet.] */ explode(obj->ox, obj->oy, -(obj->otyp), rnd(dmg), WAND_CLASS, EXPL_MAGICAL); /* this makes it hit us last, so that we can see the action first */ @@ -2942,23 +2946,17 @@ do_break_wand(obj) /* u.ux,u.uy creates it near you--x,y might create it in rock */ (void) makemon((struct permonst *)0, u.ux, u.uy, NO_MM_FLAGS); continue; - } else { - if (x == u.ux && y == u.uy) { - /* teleport objects first to avoid race with tele control and - autopickup. Other wand/object effects handled after - possible wand damage is assessed */ - if (obj->otyp == WAN_TELEPORTATION && - affects_objects && level.objects[x][y]) { - (void) bhitpile(obj, bhito, x, y); - if (context.botl) bot(); /* potion effects */ - } - damage = zapyourself(obj, FALSE); - if (damage) { - Sprintf(buf, "killed %sself by breaking a wand", uhim()); - losehp(Maybe_Half_Phys(damage), buf, NO_KILLER_PREFIX); - } - if (context.botl) bot(); /* blindness */ - } else if ((mon = m_at(x, y)) != 0) { + } else if (x != u.ux || y != u.uy) { + /* + * Wand breakage is targetting a square adjacent to the hero, + * which might contain a monster or a pile of objects or both. + * Handle objects last; avoids having undead turning raise an + * undead's corpse and then attack resulting undead monster. + * obj->bypass in bhitm() prevents the polymorphing of items + * dropped due to monster's polymorph and prevents undead + * turning that kills an undead from raising resulting corpse. + */ + if ((mon = m_at(x, y)) != 0) { (void) bhitm(mon, obj); /* if (context.botl) bot(); */ } @@ -2966,6 +2964,29 @@ do_break_wand(obj) (void) bhitpile(obj, bhito, x, y); if (context.botl) bot(); /* potion effects */ } + } else { + /* + * Wand breakage is targetting the hero. Using xdir[]+ydir[] + * deltas for location selection causes this case to happen + * after all the surrounding squares have been handled. + * Process objects first, in case damage is fatal and leaves + * bones, or teleportation sends one or more of the objects to + * same destination as hero (lookhere/autopickup); also avoids + * the polymorphing of gear dropped due to hero's transformation. + * (Unlike with monsters being hit by zaps, we can't rely on use + * of obj->bypass in the zap code to accomplish that last case + * since it's also used by retouch_equipment() for polyself.) + */ + if (affects_objects && level.objects[x][y]) { + (void) bhitpile(obj, bhito, x, y); + if (context.botl) bot(); /* potion effects */ + } + damage = zapyourself(obj, FALSE); + if (damage) { + Sprintf(buf, "killed %sself by breaking a wand", uhim()); + losehp(Maybe_Half_Phys(damage), buf, NO_KILLER_PREFIX); + } + if (context.botl) bot(); /* blindness */ } } diff --git a/src/zap.c b/src/zap.c index 7047faffd..4fd68681e 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)zap.c 3.5 2008/03/19 */ +/* SCCS Id: @(#)zap.c 3.5 2008/10/14 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1088,6 +1088,8 @@ struct obj *obj; { int zap_odds; + if (context.bypasses && obj->bypass) return FALSE; + if (obj->oclass == WAND_CLASS) zap_odds = 3; /* half-life = 2 zaps */ else if (obj->cursed) @@ -1117,6 +1119,7 @@ polyuse(objhdr, mat, minwt) for(otmp = objhdr; minwt > 0 && otmp; otmp = otmp2) { otmp2 = otmp->nexthere; + if (context.bypasses && otmp->bypass) continue; if (otmp == uball || otmp == uchain) continue; if (obj_resists(otmp, 0, 0)) continue; /* preserve unique objects */ #ifdef MAIL @@ -1156,6 +1159,14 @@ create_polymon(obj, okind) const char *material; int pm_index; + if (context.bypasses) { + /* this is approximate because the "no golems" !obj->nexthere + check below doesn't understand bypassed objects; but it + should suffice since bypassed objects always end up as a + consecutive group at the top of their pile */ + while (obj && obj->bypass) obj = obj->nexthere; + } + /* no golems if you zap only one object -- not enough stuff */ if(!obj || (!obj->nexthere && obj->quan == 1L)) return;