1 /* NetHack 3.6	zap.c	$NHDT-Date: 1573688696 2019/11/13 23:44:56 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.316 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2013. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 
8 /* Disintegration rays have special treatment; corpses are never left.
9  * But the routine which calculates the damage is separate from the routine
10  * which kills the monster.  The damage routine returns this cookie to
11  * indicate that the monster should be disintegrated.
12  */
13 #define MAGIC_COOKIE 1000
14 
15 static NEARDATA boolean obj_zapped;
16 static NEARDATA int poly_zapped;
17 
18 extern boolean notonhead; /* for long worms */
19 
20 /* kludge to use mondied instead of killed */
21 extern boolean m_using;
22 
23 STATIC_DCL void FDECL(polyuse, (struct obj *, int, int));
24 STATIC_DCL void FDECL(create_polymon, (struct obj *, int));
25 STATIC_DCL int FDECL(stone_to_flesh_obj, (struct obj *));
26 STATIC_DCL boolean FDECL(zap_updown, (struct obj *));
27 STATIC_DCL void FDECL(zhitu, (int, int, const char *, XCHAR_P, XCHAR_P));
28 STATIC_DCL void FDECL(revive_egg, (struct obj *));
29 STATIC_DCL boolean FDECL(zap_steed, (struct obj *));
30 STATIC_DCL void FDECL(skiprange, (int, int *, int *));
31 STATIC_DCL int FDECL(zap_hit, (int, int));
32 STATIC_OVL void FDECL(disintegrate_mon, (struct monst *, int, const char *));
33 STATIC_DCL void FDECL(backfire, (struct obj *));
34 STATIC_DCL int FDECL(spell_hit_bonus, (int));
35 STATIC_DCL void FDECL(destroy_one_item, (struct obj *, int, int));
36 STATIC_DCL void FDECL(wishcmdassist, (int));
37 
38 #define ZT_MAGIC_MISSILE (AD_MAGM - 1)
39 #define ZT_FIRE (AD_FIRE - 1)
40 #define ZT_COLD (AD_COLD - 1)
41 #define ZT_SLEEP (AD_SLEE - 1)
42 #define ZT_DEATH (AD_DISN - 1) /* or disintegration */
43 #define ZT_LIGHTNING (AD_ELEC - 1)
44 #define ZT_POISON_GAS (AD_DRST - 1)
45 #define ZT_ACID (AD_ACID - 1)
46 /* 8 and 9 are currently unassigned */
47 
48 #define ZT_WAND(x) (x)
49 #define ZT_SPELL(x) (10 + (x))
50 #define ZT_BREATH(x) (20 + (x))
51 
52 #define is_hero_spell(type) ((type) >= 10 && (type) < 20)
53 
54 #define M_IN_WATER(ptr) \
55     ((ptr)->mlet == S_EEL || amphibious(ptr) || is_swimmer(ptr))
56 
57 STATIC_VAR const char are_blinded_by_the_flash[] =
58     "are blinded by the flash!";
59 
60 const char *const flash_types[] =       /* also used in buzzmu(mcastu.c) */
61     {
62         "magic missile", /* Wands must be 0-9 */
63         "bolt of fire", "bolt of cold", "sleep ray", "death ray",
64         "bolt of lightning", "", "", "", "",
65 
66         "magic missile", /* Spell equivalents must be 10-19 */
67         "fireball", "cone of cold", "sleep ray", "finger of death",
68         "bolt of lightning", /* there is no spell, used for retribution */
69         "", "", "", "",
70 
71         "blast of missiles", /* Dragon breath equivalents 20-29*/
72         "blast of fire", "blast of frost", "blast of sleep gas",
73         "blast of disintegration", "blast of lightning",
74         "blast of poison gas", "blast of acid", "", ""
75     };
76 
77 /*
78  * Recognizing unseen wands by zapping:  in 3.4.3 and earlier, zapping
79  * most wand types while blind would add that type to the discoveries
80  * list even if it had never been seen (ie, picked up while blinded
81  * and shown in inventory as simply "a wand").  This behavior has been
82  * changed; now such wands won't be discovered.  But if the type is
83  * already discovered, then the individual wand whose effect was just
84  * observed will be flagged as if seen.  [You already know wands of
85  * striking; you zap "a wand" and observe striking effect (presumably
86  * by sound or touch); it'll become shown in inventory as "a wand of
87  * striking".]
88  *
89  * Unfortunately, the new behavior isn't really correct either.  There
90  * should be an `eknown' bit for "effect known" added for wands (and
91  * for potions since quaffing one of a stack is similar) so that the
92  * particular wand which has been zapped would have its type become
93  * known (it would change from "a wand" to "a wand of striking", for
94  * example) without the type becoming discovered or other unknown wands
95  * of that type showing additional information.  When blindness ends,
96  * all objects in inventory with the eknown bit set would be discovered
97  * and other items of the same type would become known as such.
98  */
99 
100 /* wand discovery gets special handling when hero is blinded */
101 void
learnwand(obj)102 learnwand(obj)
103 struct obj *obj;
104 {
105     /* For a wand (or wand-like tool) zapped by the player, if the
106        effect was observable (determined by caller; usually seen, but
107        possibly heard or felt if the hero is blinded) then discover the
108        object type provided that the object itself is known (as more
109        than just "a wand").  If object type is already discovered and
110        we observed the effect, mark the individual wand as having been
111        seen.  Suppress spells (which use fake spellbook object for `obj')
112        so that casting a spell won't re-discover its forgotten book. */
113     if (obj->oclass != SPBOOK_CLASS) {
114         /* if type already discovered, treat this item has having been seen
115            even if hero is currently blinded (skips redundant makeknown) */
116         if (objects[obj->otyp].oc_name_known) {
117             obj->dknown = 1; /* will usually be set already */
118 
119         /* otherwise discover it if item itself has been or can be seen */
120         } else {
121             /* in case it was picked up while blind and then zapped without
122                examining inventory after regaining sight (bypassing xname) */
123             if (!Blind)
124                 obj->dknown = 1;
125             /* make the discovery iff we know what we're manipulating */
126             if (obj->dknown)
127                 makeknown(obj->otyp);
128         }
129         update_inventory();
130     }
131 }
132 
133 /* Routines for IMMEDIATE wands and spells. */
134 /* bhitm: monster mtmp was hit by the effect of wand or spell otmp */
135 int
bhitm(mtmp,otmp)136 bhitm(mtmp, otmp)
137 struct monst *mtmp;
138 struct obj *otmp;
139 {
140     boolean wake = TRUE; /* Most 'zaps' should wake monster */
141     boolean reveal_invis = FALSE, learn_it = FALSE;
142     boolean dbldam = Role_if(PM_KNIGHT) && u.uhave.questart;
143     boolean skilled_spell, helpful_gesture = FALSE;
144     int dmg, otyp = otmp->otyp;
145     const char *zap_type_text = "spell";
146     struct obj *obj;
147     boolean disguised_mimic = (mtmp->data->mlet == S_MIMIC
148                                && M_AP_TYPE(mtmp) != M_AP_NOTHING);
149 
150     if (u.uswallow && mtmp == u.ustuck)
151         reveal_invis = FALSE;
152 
153     notonhead = (mtmp->mx != bhitpos.x || mtmp->my != bhitpos.y);
154     skilled_spell = (otmp && otmp->oclass == SPBOOK_CLASS && otmp->blessed);
155 
156     switch (otyp) {
157     case WAN_STRIKING:
158         zap_type_text = "wand";
159     /*FALLTHRU*/
160     case SPE_FORCE_BOLT:
161         reveal_invis = TRUE;
162         if (disguised_mimic)
163             seemimic(mtmp);
164         if (resists_magm(mtmp)) { /* match effect on player */
165             shieldeff(mtmp->mx, mtmp->my);
166             pline("Boing!");
167             break; /* skip makeknown */
168         } else if (u.uswallow || rnd(20) < 10 + find_mac(mtmp)) {
169             dmg = d(2, 12);
170             if (dbldam)
171                 dmg *= 2;
172             if (otyp == SPE_FORCE_BOLT)
173                 dmg = spell_damage_bonus(dmg);
174             hit(zap_type_text, mtmp, exclam(dmg));
175             (void) resist(mtmp, otmp->oclass, dmg, TELL);
176         } else
177             miss(zap_type_text, mtmp);
178         learn_it = TRUE;
179         break;
180     case WAN_SLOW_MONSTER:
181     case SPE_SLOW_MONSTER:
182         if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
183             if (disguised_mimic)
184                 seemimic(mtmp);
185             mon_adjust_speed(mtmp, -1, otmp);
186             m_dowear(mtmp, FALSE); /* might want speed boots */
187             if (u.uswallow && (mtmp == u.ustuck) && is_whirly(mtmp->data)) {
188                 You("disrupt %s!", mon_nam(mtmp));
189                 pline("A huge hole opens up...");
190                 expels(mtmp, mtmp->data, TRUE);
191             }
192         }
193         break;
194     case WAN_SPEED_MONSTER:
195         if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
196             if (disguised_mimic)
197                 seemimic(mtmp);
198             mon_adjust_speed(mtmp, 1, otmp);
199             m_dowear(mtmp, FALSE); /* might want speed boots */
200         }
201         if (mtmp->mtame)
202             helpful_gesture = TRUE;
203         break;
204     case WAN_UNDEAD_TURNING:
205     case SPE_TURN_UNDEAD:
206         wake = FALSE;
207         if (unturn_dead(mtmp))
208             wake = TRUE;
209         if (is_undead(mtmp->data) || is_vampshifter(mtmp)) {
210             reveal_invis = TRUE;
211             wake = TRUE;
212             dmg = rnd(8);
213             if (dbldam)
214                 dmg *= 2;
215             if (otyp == SPE_TURN_UNDEAD)
216                 dmg = spell_damage_bonus(dmg);
217             context.bypasses = TRUE; /* for make_corpse() */
218             if (!resist(mtmp, otmp->oclass, dmg, NOTELL)) {
219                 if (!DEADMONSTER(mtmp))
220                     monflee(mtmp, 0, FALSE, TRUE);
221             }
222         }
223         break;
224     case WAN_POLYMORPH:
225     case SPE_POLYMORPH:
226     case POT_POLYMORPH:
227         if (mtmp->data == &mons[PM_LONG_WORM] && has_mcorpsenm(mtmp)) {
228             /* if a long worm has mcorpsenm set, it was polymophed by
229                the current zap and shouldn't be affected if hit again */
230             ;
231         } else if (resists_magm(mtmp)) {
232             /* magic resistance protects from polymorph traps, so make
233                it guard against involuntary polymorph attacks too... */
234             shieldeff(mtmp->mx, mtmp->my);
235         } else if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
236             boolean polyspot = (otyp != POT_POLYMORPH),
237                     give_msg = (!Hallucination
238                                 && (canseemon(mtmp)
239                                     || (u.uswallow && mtmp == u.ustuck)));
240 
241             /* dropped inventory (due to death by system shock,
242                or loss of wielded weapon and/or worn armor due to
243                limitations of new shape) won't be hit by this zap */
244             if (polyspot)
245                 for (obj = mtmp->minvent; obj; obj = obj->nobj)
246                     bypass_obj(obj);
247 
248             /* natural shapechangers aren't affected by system shock
249                (unless protection from shapechangers is interfering
250                with their metabolism...) */
251             if (mtmp->cham == NON_PM && !rn2(25)) {
252                 if (canseemon(mtmp)) {
253                     pline("%s shudders!", Monnam(mtmp));
254                     learn_it = TRUE;
255                 }
256                 /* context.bypasses = TRUE; ## for make_corpse() */
257                 /* no corpse after system shock */
258                 xkilled(mtmp, XKILL_GIVEMSG | XKILL_NOCORPSE);
259             } else if (newcham(mtmp, (struct permonst *) 0,
260                                polyspot, give_msg) != 0
261                        /* if shapechange failed because there aren't
262                           enough eligible candidates (most likely for
263                           vampshifter), try reverting to original form */
264                        || (mtmp->cham >= LOW_PM
265                            && newcham(mtmp, &mons[mtmp->cham],
266                                       polyspot, give_msg) != 0)) {
267                 if (give_msg && (canspotmon(mtmp)
268                                  || (u.uswallow && mtmp == u.ustuck)))
269                     learn_it = TRUE;
270             }
271 
272             /* do this even if polymorphed failed (otherwise using
273                flags.mon_polycontrol prompting to force mtmp to remain
274                'long worm' would prompt again if zap hit another segment) */
275             if (!DEADMONSTER(mtmp) && mtmp->data == &mons[PM_LONG_WORM]) {
276                 if (!has_mcorpsenm(mtmp))
277                     newmcorpsenm(mtmp);
278                 /* flag to indicate that mtmp became a long worm
279                    on current zap, so further hits (on mtmp's new
280                    tail) don't do further transforms */
281                 MCORPSENM(mtmp) = PM_LONG_WORM;
282                 /* flag to indicate that cleanup is needed; object
283                    bypass cleanup also clears mon->mextra->mcorpsenm
284                    for all long worms on the level */
285                 context.bypasses = TRUE;
286             }
287         }
288         break;
289     case WAN_CANCELLATION:
290     case SPE_CANCELLATION:
291         if (disguised_mimic)
292             seemimic(mtmp);
293         (void) cancel_monst(mtmp, otmp, TRUE, TRUE, FALSE);
294         break;
295     case WAN_TELEPORTATION:
296     case SPE_TELEPORT_AWAY:
297         if (disguised_mimic)
298             seemimic(mtmp);
299         reveal_invis = !u_teleport_mon(mtmp, TRUE);
300         break;
301     case WAN_MAKE_INVISIBLE: {
302         int oldinvis = mtmp->minvis;
303         char nambuf[BUFSZ];
304 
305         if (disguised_mimic)
306             seemimic(mtmp);
307         /* format monster's name before altering its visibility */
308         Strcpy(nambuf, Monnam(mtmp));
309         mon_set_minvis(mtmp);
310         if (!oldinvis && knowninvisible(mtmp)) {
311             pline("%s turns transparent!", nambuf);
312             reveal_invis = TRUE;
313             learn_it = TRUE;
314         }
315         break;
316     }
317     case WAN_LOCKING:
318     case SPE_WIZARD_LOCK:
319         wake = closeholdingtrap(mtmp, &learn_it);
320         break;
321     case WAN_PROBING:
322         wake = FALSE;
323         reveal_invis = TRUE;
324         probe_monster(mtmp);
325         learn_it = TRUE;
326         break;
327     case WAN_OPENING:
328     case SPE_KNOCK:
329         wake = FALSE; /* don't want immediate counterattack */
330         if (u.uswallow && mtmp == u.ustuck) {
331             if (is_animal(mtmp->data)) {
332                 if (Blind)
333                     You_feel("a sudden rush of air!");
334                 else
335                     pline("%s opens its mouth!", Monnam(mtmp));
336             }
337             expels(mtmp, mtmp->data, TRUE);
338             /* zap which hits steed will only release saddle if it
339                doesn't hit a holding or falling trap; playability
340                here overrides the more logical target ordering */
341         } else if (openholdingtrap(mtmp, &learn_it)) {
342             break;
343         } else if (openfallingtrap(mtmp, TRUE, &learn_it)) {
344             /* mtmp might now be on the migrating monsters list */
345             break;
346         } else if ((obj = which_armor(mtmp, W_SADDLE)) != 0) {
347             char buf[BUFSZ];
348 
349             Sprintf(buf, "%s %s", s_suffix(Monnam(mtmp)),
350                     distant_name(obj, xname));
351             if (cansee(mtmp->mx, mtmp->my)) {
352                 if (!canspotmon(mtmp))
353                     Strcpy(buf, An(distant_name(obj, xname)));
354                 pline("%s falls to the %s.", buf,
355                       surface(mtmp->mx, mtmp->my));
356             } else if (canspotmon(mtmp)) {
357                 pline("%s falls off.", buf);
358             }
359             obj_extract_self(obj);
360             mdrop_obj(mtmp, obj, FALSE);
361         }
362         break;
363     case SPE_HEALING:
364     case SPE_EXTRA_HEALING:
365         reveal_invis = TRUE;
366         if (mtmp->data != &mons[PM_PESTILENCE]) {
367             wake = FALSE; /* wakeup() makes the target angry */
368             mtmp->mhp += d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4);
369             if (mtmp->mhp > mtmp->mhpmax)
370                 mtmp->mhp = mtmp->mhpmax;
371             /* plain healing must be blessed to cure blindness; extra
372                healing only needs to not be cursed, so spell always cures
373                [potions quaffed by monsters behave slightly differently;
374                we use the rules for the hero here...] */
375             if (skilled_spell || otyp == SPE_EXTRA_HEALING)
376                 mcureblindness(mtmp, canseemon(mtmp));
377             if (canseemon(mtmp)) {
378                 if (disguised_mimic) {
379                     if (is_obj_mappear(mtmp,STRANGE_OBJECT)) {
380                         /* it can do better now */
381                         set_mimic_sym(mtmp);
382                         newsym(mtmp->mx, mtmp->my);
383                     } else
384                         mimic_hit_msg(mtmp, otyp);
385                 } else
386                     pline("%s looks%s better.", Monnam(mtmp),
387                           otyp == SPE_EXTRA_HEALING ? " much" : "");
388             }
389             if (mtmp->mtame || mtmp->mpeaceful) {
390                 adjalign(Role_if(PM_HEALER) ? 1 : sgn(u.ualign.type));
391             }
392         } else { /* Pestilence */
393             /* Pestilence will always resist; damage is half of 3d{4,8} */
394             (void) resist(mtmp, otmp->oclass,
395                           d(3, otyp == SPE_EXTRA_HEALING ? 8 : 4), TELL);
396         }
397         break;
398     case WAN_LIGHT: /* (broken wand) */
399         if (flash_hits_mon(mtmp, otmp)) {
400             learn_it = TRUE;
401             reveal_invis = TRUE;
402         }
403         break;
404     case WAN_SLEEP: /* (broken wand) */
405         /* [wakeup() doesn't rouse victims of temporary sleep,
406            so it's okay to leave `wake' set to TRUE here] */
407         reveal_invis = TRUE;
408         if (sleep_monst(mtmp, d(1 + otmp->spe, 12), WAND_CLASS))
409             slept_monst(mtmp);
410         if (!Blind)
411             learn_it = TRUE;
412         break;
413     case SPE_STONE_TO_FLESH:
414         if (monsndx(mtmp->data) == PM_STONE_GOLEM) {
415             char *name = Monnam(mtmp);
416 
417             /* turn into flesh golem */
418             if (newcham(mtmp, &mons[PM_FLESH_GOLEM], FALSE, FALSE)) {
419                 if (canseemon(mtmp))
420                     pline("%s turns to flesh!", name);
421             } else {
422                 if (canseemon(mtmp))
423                     pline("%s looks rather fleshy for a moment.", name);
424             }
425         } else
426             wake = FALSE;
427         break;
428     case SPE_DRAIN_LIFE:
429         if (disguised_mimic)
430             seemimic(mtmp);
431         dmg = monhp_per_lvl(mtmp);
432         if (dbldam)
433             dmg *= 2;
434         if (otyp == SPE_DRAIN_LIFE)
435             dmg = spell_damage_bonus(dmg);
436         if (resists_drli(mtmp)) {
437             shieldeff(mtmp->mx, mtmp->my);
438         } else if (!resist(mtmp, otmp->oclass, dmg, NOTELL)
439                    && !DEADMONSTER(mtmp)) {
440             mtmp->mhp -= dmg;
441             mtmp->mhpmax -= dmg;
442             /* die if already level 0, regardless of hit points */
443             if (DEADMONSTER(mtmp) || mtmp->mhpmax <= 0 || mtmp->m_lev < 1) {
444                 killed(mtmp);
445             } else {
446                 mtmp->m_lev--;
447                 if (canseemon(mtmp))
448                     pline("%s suddenly seems weaker!", Monnam(mtmp));
449             }
450         }
451         break;
452     case WAN_NOTHING:
453         wake = FALSE;
454         break;
455     default:
456         impossible("What an interesting effect (%d)", otyp);
457         break;
458     }
459     if (wake) {
460         if (!DEADMONSTER(mtmp)) {
461             wakeup(mtmp, helpful_gesture ? FALSE : TRUE);
462             m_respond(mtmp);
463             if (mtmp->isshk && !*u.ushops)
464                 hot_pursuit(mtmp);
465         } else if (M_AP_TYPE(mtmp))
466             seemimic(mtmp); /* might unblock if mimicing a boulder/door */
467     }
468     /* note: bhitpos won't be set if swallowed, but that's okay since
469      * reveal_invis will be false.  We can't use mtmp->mx, my since it
470      * might be an invisible worm hit on the tail.
471      */
472     if (reveal_invis) {
473         if (!DEADMONSTER(mtmp) && cansee(bhitpos.x, bhitpos.y)
474             && !canspotmon(mtmp))
475             map_invisible(bhitpos.x, bhitpos.y);
476     }
477     /* if effect was observable then discover the wand type provided
478        that the wand itself has been seen */
479     if (learn_it)
480         learnwand(otmp);
481     return 0;
482 }
483 
484 void
probe_monster(mtmp)485 probe_monster(mtmp)
486 struct monst *mtmp;
487 {
488     struct obj *otmp;
489 
490     mstatusline(mtmp);
491     if (notonhead)
492         return; /* don't show minvent for long worm tail */
493 
494     if (mtmp->minvent) {
495         for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) {
496             otmp->dknown = 1; /* treat as "seen" */
497             if (Is_container(otmp) || otmp->otyp == STATUE) {
498                 otmp->lknown = 1;
499                 if (!SchroedingersBox(otmp))
500                     otmp->cknown = 1;
501             }
502         }
503         (void) display_minventory(mtmp, MINV_ALL | MINV_NOLET | PICK_NONE,
504                                   (char *) 0);
505     } else {
506         pline("%s is not carrying anything%s.", noit_Monnam(mtmp),
507               (u.uswallow && mtmp == u.ustuck) ? " besides you" : "");
508     }
509 }
510 
511 /*
512  * Return the object's physical location.  This only makes sense for
513  * objects that are currently on the level (i.e. migrating objects
514  * are nowhere).  By default, only things that can be seen (in hero's
515  * inventory, monster's inventory, or on the ground) are reported.
516  * By adding BURIED_TOO and/or CONTAINED_TOO flags, you can also get
517  * the location of buried and contained objects.  Note that if an
518  * object is carried by a monster, its reported position may change
519  * from turn to turn.  This function returns FALSE if the position
520  * is not available or subject to the constraints above.
521  */
522 boolean
get_obj_location(obj,xp,yp,locflags)523 get_obj_location(obj, xp, yp, locflags)
524 struct obj *obj;
525 xchar *xp, *yp;
526 int locflags;
527 {
528     switch (obj->where) {
529     case OBJ_INVENT:
530         *xp = u.ux;
531         *yp = u.uy;
532         return TRUE;
533     case OBJ_FLOOR:
534         *xp = obj->ox;
535         *yp = obj->oy;
536         return TRUE;
537     case OBJ_MINVENT:
538         if (obj->ocarry->mx) {
539             *xp = obj->ocarry->mx;
540             *yp = obj->ocarry->my;
541             return TRUE;
542         }
543         break; /* !mx => migrating monster */
544     case OBJ_BURIED:
545         if (locflags & BURIED_TOO) {
546             *xp = obj->ox;
547             *yp = obj->oy;
548             return TRUE;
549         }
550         break;
551     case OBJ_CONTAINED:
552         if (locflags & CONTAINED_TOO)
553             return get_obj_location(obj->ocontainer, xp, yp, locflags);
554         break;
555     }
556     *xp = *yp = 0;
557     return FALSE;
558 }
559 
560 boolean
get_mon_location(mon,xp,yp,locflags)561 get_mon_location(mon, xp, yp, locflags)
562 struct monst *mon;
563 xchar *xp, *yp;
564 int locflags; /* non-zero means get location even if monster is buried */
565 {
566     if (mon == &youmonst) {
567         *xp = u.ux;
568         *yp = u.uy;
569         return TRUE;
570     } else if (mon->mx > 0 && (!mon->mburied || locflags)) {
571         *xp = mon->mx;
572         *yp = mon->my;
573         return TRUE;
574     } else { /* migrating or buried */
575         *xp = *yp = 0;
576         return FALSE;
577     }
578 }
579 
580 /* used by revive() and animate_statue() */
581 struct monst *
montraits(obj,cc,adjacentok)582 montraits(obj, cc, adjacentok)
583 struct obj *obj;
584 coord *cc;
585 boolean adjacentok; /* False: at obj's spot only, True: nearby is allowed */
586 {
587     struct monst *mtmp, *mtmp2 = has_omonst(obj) ? get_mtraits(obj, TRUE) : 0;
588 
589     if (mtmp2) {
590         /* save_mtraits() validated mtmp2->mnum */
591         mtmp2->data = &mons[mtmp2->mnum];
592         if (mtmp2->mhpmax <= 0 && !is_rider(mtmp2->data))
593             return (struct monst *) 0;
594         mtmp = makemon(mtmp2->data, cc->x, cc->y,
595                        (NO_MINVENT | MM_NOWAIT | MM_NOCOUNTBIRTH
596                         | (adjacentok ? MM_ADJACENTOK : 0)));
597         if (!mtmp) {
598             /* mtmp2 is a copy of obj's object->oextra->omonst extension
599                and is not on the map or on any monst lists */
600             dealloc_monst(mtmp2);
601             return (struct monst *) 0;
602         }
603 
604         /* heal the monster */
605         if (mtmp->mhpmax > mtmp2->mhpmax && is_rider(mtmp2->data))
606             mtmp2->mhpmax = mtmp->mhpmax;
607         mtmp2->mhp = mtmp2->mhpmax;
608         /* Get these ones from mtmp */
609         mtmp2->minvent = mtmp->minvent; /*redundant*/
610         /* monster ID is available if the monster died in the current
611            game, but will be zero if the corpse was in a bones level
612            (we cleared it when loading bones) */
613         if (mtmp->m_id) {
614             mtmp2->m_id = mtmp->m_id;
615             /* might be bringing quest leader back to life */
616             if (quest_status.leader_is_dead
617                 /* leader_is_dead implies leader_m_id is valid */
618                 && mtmp2->m_id == quest_status.leader_m_id)
619                 quest_status.leader_is_dead = FALSE;
620         }
621         mtmp2->mx = mtmp->mx;
622         mtmp2->my = mtmp->my;
623         mtmp2->mux = mtmp->mux;
624         mtmp2->muy = mtmp->muy;
625         mtmp2->mw = mtmp->mw;
626         mtmp2->wormno = mtmp->wormno;
627         mtmp2->misc_worn_check = mtmp->misc_worn_check;
628         mtmp2->weapon_check = mtmp->weapon_check;
629         mtmp2->mtrapseen = mtmp->mtrapseen;
630         mtmp2->mflee = mtmp->mflee;
631         mtmp2->mburied = mtmp->mburied;
632         mtmp2->mundetected = mtmp->mundetected;
633         mtmp2->mfleetim = mtmp->mfleetim;
634         mtmp2->mlstmv = mtmp->mlstmv;
635         mtmp2->m_ap_type = mtmp->m_ap_type;
636         /* set these ones explicitly */
637         mtmp2->mrevived = 1;
638         mtmp2->mavenge = 0;
639         mtmp2->meating = 0;
640         mtmp2->mleashed = 0;
641         mtmp2->mtrapped = 0;
642         mtmp2->msleeping = 0;
643         mtmp2->mfrozen = 0;
644         mtmp2->mcanmove = 1;
645         /* most cancelled monsters return to normal,
646            but some need to stay cancelled */
647         if (!dmgtype(mtmp2->data, AD_SEDU)
648             && (!SYSOPT_SEDUCE || !dmgtype(mtmp2->data, AD_SSEX)))
649             mtmp2->mcan = 0;
650         mtmp2->mcansee = 1; /* set like in makemon */
651         mtmp2->mblinded = 0;
652         mtmp2->mstun = 0;
653         mtmp2->mconf = 0;
654         /* when traits are for a shopeekper, dummy monster 'mtmp' won't
655            have necessary eshk data for replmon() -> replshk() */
656         if (mtmp2->isshk) {
657             neweshk(mtmp);
658             *ESHK(mtmp) = *ESHK(mtmp2);
659             if (ESHK(mtmp2)->bill_p != 0
660                 && ESHK(mtmp2)->bill_p != (struct bill_x *) -1000)
661                 ESHK(mtmp)->bill_p = &(ESHK(mtmp)->bill[0]);
662             mtmp->isshk = 1;
663         }
664         replmon(mtmp, mtmp2);
665         newsym(mtmp2->mx, mtmp2->my); /* Might now be invisible */
666 
667         /* in case Protection_from_shape_changers is different
668            now than it was when the traits were stored */
669         restore_cham(mtmp2);
670     }
671     return mtmp2;
672 }
673 
674 /*
675  * get_container_location() returns the following information
676  * about the outermost container:
677  * loc argument gets set to:
678  *      OBJ_INVENT      if in hero's inventory; return 0.
679  *      OBJ_FLOOR       if on the floor; return 0.
680  *      OBJ_BURIED      if buried; return 0.
681  *      OBJ_MINVENT     if in monster's inventory; return monster.
682  * container_nesting is updated with the nesting depth of the containers
683  * if applicable.
684  */
685 struct monst *
get_container_location(obj,loc,container_nesting)686 get_container_location(obj, loc, container_nesting)
687 struct obj *obj;
688 int *loc;
689 int *container_nesting;
690 {
691     if (!obj || !loc)
692         return 0;
693 
694     if (container_nesting)
695         *container_nesting = 0;
696     while (obj && obj->where == OBJ_CONTAINED) {
697         if (container_nesting)
698             *container_nesting += 1;
699         obj = obj->ocontainer;
700     }
701     if (obj) {
702         *loc = obj->where; /* outermost container's location */
703         if (obj->where == OBJ_MINVENT)
704             return obj->ocarry;
705     }
706     return (struct monst *) 0;
707 }
708 
709 /*
710  * Attempt to revive the given corpse, return the revived monster if
711  * successful.  Note: this does NOT use up the corpse if it fails.
712  * If corpse->quan is more than 1, only one corpse will be affected
713  * and only one monster will be resurrected.
714  */
715 struct monst *
revive(corpse,by_hero)716 revive(corpse, by_hero)
717 struct obj *corpse;
718 boolean by_hero;
719 {
720     struct monst *mtmp = 0;
721     struct permonst *mptr;
722     struct obj *container;
723     coord xy;
724     xchar x, y;
725     boolean one_of;
726     int montype, container_nesting = 0;
727 
728     if (corpse->otyp != CORPSE) {
729         impossible("Attempting to revive %s?", xname(corpse));
730         return (struct monst *) 0;
731     }
732 
733     x = y = 0;
734     if (corpse->where != OBJ_CONTAINED) {
735         /* only for invent, minvent, or floor */
736         container = 0;
737         (void) get_obj_location(corpse, &x, &y, 0);
738     } else {
739         /* deal with corpses in [possibly nested] containers */
740         struct monst *carrier;
741         int holder = OBJ_FREE;
742 
743         container = corpse->ocontainer;
744         carrier =
745             get_container_location(container, &holder, &container_nesting);
746         switch (holder) {
747         case OBJ_MINVENT:
748             x = carrier->mx, y = carrier->my;
749             break;
750         case OBJ_INVENT:
751             x = u.ux, y = u.uy;
752             break;
753         case OBJ_FLOOR:
754             (void) get_obj_location(corpse, &x, &y, CONTAINED_TOO);
755             break;
756         default:
757             break; /* x,y are 0 */
758         }
759     }
760     if (!x || !y
761         /* Rules for revival from containers:
762          *  - the container cannot be locked
763          *  - the container cannot be heavily nested (>2 is arbitrary)
764          *  - the container cannot be a statue or bag of holding
765          *    (except in very rare cases for the latter)
766          */
767         || (container && (container->olocked || container_nesting > 2
768                           || container->otyp == STATUE
769                           || (container->otyp == BAG_OF_HOLDING && rn2(40)))))
770         return (struct monst *) 0;
771 
772     /* record the object's location now that we're sure where it is */
773     corpse->ox = x, corpse->oy = y;
774 
775     /* prepare for the monster */
776     montype = corpse->corpsenm;
777     mptr = &mons[montype];
778     /* [should probably handle recorporealization first; if corpse and
779        ghost are at same location, revived creature shouldn't be bumped
780        to an adjacent spot by ghost which joins with it] */
781     if (MON_AT(x, y)) {
782         if (enexto(&xy, x, y, mptr))
783             x = xy.x, y = xy.y;
784     }
785 
786     if ((mons[montype].mlet == S_EEL && !IS_POOL(levl[x][y].typ))
787         || (mons[montype].mlet == S_TROLL
788             && uwep && uwep->oartifact == ART_TROLLSBANE)) {
789         if (by_hero && cansee(x, y))
790             pline("%s twitches feebly.",
791                 upstart(corpse_xname(corpse, (const char *) 0, CXN_PFX_THE)));
792         return (struct monst *) 0;
793     }
794 
795     if (cant_revive(&montype, TRUE, corpse)) {
796         /* make a zombie or doppelganger instead */
797         /* note: montype has changed; mptr keeps old value for newcham() */
798         mtmp = makemon(&mons[montype], x, y, NO_MINVENT | MM_NOWAIT);
799         if (mtmp) {
800             /* skip ghost handling */
801             if (has_omid(corpse))
802                 free_omid(corpse);
803             if (has_omonst(corpse))
804                 free_omonst(corpse);
805             if (mtmp->cham == PM_DOPPELGANGER) {
806                 /* change shape to match the corpse */
807                 (void) newcham(mtmp, mptr, FALSE, FALSE);
808             } else if (mtmp->data->mlet == S_ZOMBIE) {
809                 mtmp->mhp = mtmp->mhpmax = 100;
810                 mon_adjust_speed(mtmp, 2, (struct obj *) 0); /* MFAST */
811             }
812         }
813     } else if (has_omonst(corpse)) {
814         /* use saved traits */
815         xy.x = x, xy.y = y;
816         mtmp = montraits(corpse, &xy, FALSE);
817         if (mtmp && mtmp->mtame && !mtmp->isminion)
818             wary_dog(mtmp, TRUE);
819     } else {
820         /* make a new monster */
821         mtmp = makemon(mptr, x, y, NO_MINVENT | MM_NOWAIT | MM_NOCOUNTBIRTH);
822     }
823     if (!mtmp)
824         return (struct monst *) 0;
825 
826     /* hiders shouldn't already be re-hidden when they revive */
827     if (mtmp->mundetected) {
828         mtmp->mundetected = 0;
829         newsym(mtmp->mx, mtmp->my);
830     }
831     if (M_AP_TYPE(mtmp))
832         seemimic(mtmp);
833 
834     one_of = (corpse->quan > 1L);
835     if (one_of)
836         corpse = splitobj(corpse, 1L);
837 
838     /* if this is caused by the hero there might be a shop charge */
839     if (by_hero) {
840         struct monst *shkp = 0;
841 
842         x = corpse->ox, y = corpse->oy;
843         if (costly_spot(x, y)
844             && (carried(corpse) ? corpse->unpaid : !corpse->no_charge))
845             shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
846 
847         if (cansee(x, y)) {
848             char buf[BUFSZ];
849 
850             Strcpy(buf, one_of ? "one of " : "");
851             /* shk_your: "the " or "your " or "<mon>'s " or "<Shk>'s ".
852                If the result is "Shk's " then it will be ambiguous:
853                is Shk the mon carrying it, or does Shk's shop own it?
854                Let's not worry about that... */
855             (void) shk_your(eos(buf), corpse);
856             if (one_of)
857                 corpse->quan++; /* force plural */
858             Strcat(buf, corpse_xname(corpse, (const char *) 0, CXN_NO_PFX));
859             if (one_of) /* could be simplified to ''corpse->quan = 1L;'' */
860                 corpse->quan--;
861             pline("%s glows iridescently.", upstart(buf));
862         } else if (shkp) {
863             /* need some prior description of the corpse since
864                stolen_value() will refer to the object as "it" */
865             pline("A corpse is resuscitated.");
866         }
867         /* don't charge for shopkeeper's own corpse if we just revived him */
868         if (shkp && mtmp != shkp)
869             (void) stolen_value(corpse, x, y, (boolean) shkp->mpeaceful,
870                                 FALSE);
871 
872         /* [we don't give any comparable message about the corpse for
873            the !by_hero case because caller might have already done so] */
874     }
875 
876     /* handle recorporealization of an active ghost */
877     if (has_omid(corpse)) {
878         unsigned m_id;
879         struct monst *ghost;
880         struct obj *otmp;
881 
882         (void) memcpy((genericptr_t) &m_id, (genericptr_t) OMID(corpse),
883                       sizeof m_id);
884         ghost = find_mid(m_id, FM_FMON);
885         if (ghost && ghost->data == &mons[PM_GHOST]) {
886             if (canseemon(ghost))
887                 pline("%s is suddenly drawn into its former body!",
888                       Monnam(ghost));
889             /* transfer the ghost's inventory along with it */
890             while ((otmp = ghost->minvent) != 0) {
891                 obj_extract_self(otmp);
892                 add_to_minv(mtmp, otmp);
893             }
894             /* tame the revived monster if its ghost was tame */
895             if (ghost->mtame && !mtmp->mtame) {
896                 if (tamedog(mtmp, (struct obj *) 0)) {
897                     /* ghost's edog data is ignored */
898                     mtmp->mtame = ghost->mtame;
899                 }
900             }
901             /* was ghost, now alive, it's all very confusing */
902             mtmp->mconf = 1;
903             /* separate ghost monster no longer exists */
904             mongone(ghost);
905         }
906         free_omid(corpse);
907     }
908 
909     /* monster retains its name */
910     if (has_oname(corpse) && !unique_corpstat(mtmp->data))
911         mtmp = christen_monst(mtmp, ONAME(corpse));
912     /* partially eaten corpse yields wounded monster */
913     if (corpse->oeaten)
914         mtmp->mhp = eaten_stat(mtmp->mhp, corpse);
915     /* track that this monster was revived at least once */
916     mtmp->mrevived = 1;
917 
918     /* finally, get rid of the corpse--it's gone now */
919     switch (corpse->where) {
920     case OBJ_INVENT:
921         useup(corpse);
922         break;
923     case OBJ_FLOOR:
924         /* in case MON_AT+enexto for invisible mon */
925         x = corpse->ox, y = corpse->oy;
926         /* not useupf(), which charges */
927         if (corpse->quan > 1L)
928             corpse = splitobj(corpse, 1L);
929         delobj(corpse);
930         newsym(x, y);
931         break;
932     case OBJ_MINVENT:
933         m_useup(corpse->ocarry, corpse);
934         break;
935     case OBJ_CONTAINED:
936         obj_extract_self(corpse);
937         obfree(corpse, (struct obj *) 0);
938         break;
939     default:
940         panic("revive");
941     }
942 
943     return mtmp;
944 }
945 
946 STATIC_OVL void
revive_egg(obj)947 revive_egg(obj)
948 struct obj *obj;
949 {
950     /*
951      * Note: generic eggs with corpsenm set to NON_PM will never hatch.
952      */
953     if (obj->otyp != EGG)
954         return;
955     if (obj->corpsenm != NON_PM && !dead_species(obj->corpsenm, TRUE))
956         attach_egg_hatch_timeout(obj, 0L);
957 }
958 
959 /* try to revive all corpses and eggs carried by `mon' */
960 int
unturn_dead(mon)961 unturn_dead(mon)
962 struct monst *mon;
963 {
964     struct obj *otmp, *otmp2;
965     struct monst *mtmp2;
966     char owner[BUFSZ], corpse[BUFSZ];
967     boolean youseeit;
968     int res = 0;
969 
970     youseeit = (mon == &youmonst) ? TRUE : canseemon(mon);
971     otmp2 = (mon == &youmonst) ? invent : mon->minvent;
972     owner[0] = corpse[0] = '\0'; /* lint suppression */
973 
974     while ((otmp = otmp2) != 0) {
975         otmp2 = otmp->nobj;
976         if (otmp->otyp == EGG)
977             revive_egg(otmp);
978         if (otmp->otyp != CORPSE)
979             continue;
980         /* save the name; the object is liable to go away */
981         if (youseeit) {
982             Strcpy(corpse,
983                    corpse_xname(otmp, (const char *) 0, CXN_SINGULAR));
984             Shk_Your(owner, otmp); /* includes a trailing space */
985         }
986 
987         /* for a stack, only one is revived */
988         if ((mtmp2 = revive(otmp, !context.mon_moving)) != 0) {
989             ++res;
990             if (youseeit)
991                 pline("%s%s suddenly comes alive!", owner, corpse);
992             else if (canseemon(mtmp2))
993                 pline("%s suddenly appears!", Amonnam(mtmp2));
994         }
995     }
996     return res;
997 }
998 
999 /* cancel obj, possibly carried by you or a monster */
1000 void
cancel_item(obj)1001 cancel_item(obj)
1002 register struct obj *obj;
1003 {
1004     boolean u_ring = (obj == uleft || obj == uright);
1005     int otyp = obj->otyp;
1006 
1007     switch (otyp) {
1008     case RIN_GAIN_STRENGTH:
1009         if ((obj->owornmask & W_RING) && u_ring) {
1010             ABON(A_STR) -= obj->spe;
1011             context.botl = 1;
1012         }
1013         break;
1014     case RIN_GAIN_CONSTITUTION:
1015         if ((obj->owornmask & W_RING) && u_ring) {
1016             ABON(A_CON) -= obj->spe;
1017             context.botl = 1;
1018         }
1019         break;
1020     case RIN_ADORNMENT:
1021         if ((obj->owornmask & W_RING) && u_ring) {
1022             ABON(A_CHA) -= obj->spe;
1023             context.botl = 1;
1024         }
1025         break;
1026     case RIN_INCREASE_ACCURACY:
1027         if ((obj->owornmask & W_RING) && u_ring)
1028             u.uhitinc -= obj->spe;
1029         break;
1030     case RIN_INCREASE_DAMAGE:
1031         if ((obj->owornmask & W_RING) && u_ring)
1032             u.udaminc -= obj->spe;
1033         break;
1034     case GAUNTLETS_OF_DEXTERITY:
1035         if ((obj->owornmask & W_ARMG) && (obj == uarmg)) {
1036             ABON(A_DEX) -= obj->spe;
1037             context.botl = 1;
1038         }
1039         break;
1040     case HELM_OF_BRILLIANCE:
1041         if ((obj->owornmask & W_ARMH) && (obj == uarmh)) {
1042             ABON(A_INT) -= obj->spe;
1043             ABON(A_WIS) -= obj->spe;
1044             context.botl = 1;
1045         }
1046         break;
1047         /* case RIN_PROTECTION:  not needed */
1048     }
1049     if (objects[otyp].oc_magic
1050         || (obj->spe && (obj->oclass == ARMOR_CLASS
1051                          || obj->oclass == WEAPON_CLASS || is_weptool(obj)))
1052         || otyp == POT_ACID
1053         || otyp == POT_SICKNESS
1054         || (otyp == POT_WATER && (obj->blessed || obj->cursed))) {
1055         if (obj->spe != ((obj->oclass == WAND_CLASS) ? -1 : 0)
1056             && otyp != WAN_CANCELLATION /* can't cancel cancellation */
1057             && otyp != MAGIC_LAMP /* cancelling doesn't remove djinni */
1058             && otyp != CANDELABRUM_OF_INVOCATION) {
1059             costly_alteration(obj, COST_CANCEL);
1060             obj->spe = (obj->oclass == WAND_CLASS) ? -1 : 0;
1061         }
1062         switch (obj->oclass) {
1063         case SCROLL_CLASS:
1064             costly_alteration(obj, COST_CANCEL);
1065             obj->otyp = SCR_BLANK_PAPER;
1066             obj->spe = 0;
1067             break;
1068         case SPBOOK_CLASS:
1069             if (otyp != SPE_CANCELLATION && otyp != SPE_NOVEL
1070                 && otyp != SPE_BOOK_OF_THE_DEAD) {
1071                 costly_alteration(obj, COST_CANCEL);
1072                 obj->otyp = SPE_BLANK_PAPER;
1073             }
1074             break;
1075         case POTION_CLASS:
1076             costly_alteration(obj,
1077                               (otyp != POT_WATER)
1078                                   ? COST_CANCEL
1079                                   : obj->cursed ? COST_UNCURS : COST_UNBLSS);
1080             if (otyp == POT_SICKNESS || otyp == POT_SEE_INVISIBLE) {
1081                 /* sickness is "biologically contaminated" fruit juice;
1082                    cancel it and it just becomes fruit juice...
1083                    whereas see invisible tastes like "enchanted" fruit
1084                    juice, it similarly cancels */
1085                 obj->otyp = POT_FRUIT_JUICE;
1086             } else {
1087                 obj->otyp = POT_WATER;
1088                 obj->odiluted = 0; /* same as any other water */
1089             }
1090             break;
1091         }
1092     }
1093     unbless(obj);
1094     uncurse(obj);
1095     return;
1096 }
1097 
1098 /* Remove a positive enchantment or charge from obj,
1099  * possibly carried by you or a monster
1100  */
1101 boolean
drain_item(obj,by_you)1102 drain_item(obj, by_you)
1103 struct obj *obj;
1104 boolean by_you;
1105 {
1106     boolean u_ring;
1107 
1108     /* Is this a charged/enchanted object? */
1109     if (!obj
1110         || (!objects[obj->otyp].oc_charged && obj->oclass != WEAPON_CLASS
1111             && obj->oclass != ARMOR_CLASS && !is_weptool(obj))
1112         || obj->spe <= 0)
1113         return FALSE;
1114     if (defends(AD_DRLI, obj) || defends_when_carried(AD_DRLI, obj)
1115         || obj_resists(obj, 10, 90))
1116         return FALSE;
1117 
1118     /* Charge for the cost of the object */
1119     if (by_you)
1120         costly_alteration(obj, COST_DRAIN);
1121 
1122     /* Drain the object and any implied effects */
1123     obj->spe--;
1124     u_ring = (obj == uleft) || (obj == uright);
1125     switch (obj->otyp) {
1126     case RIN_GAIN_STRENGTH:
1127         if ((obj->owornmask & W_RING) && u_ring) {
1128             ABON(A_STR)--;
1129             context.botl = 1;
1130         }
1131         break;
1132     case RIN_GAIN_CONSTITUTION:
1133         if ((obj->owornmask & W_RING) && u_ring) {
1134             ABON(A_CON)--;
1135             context.botl = 1;
1136         }
1137         break;
1138     case RIN_ADORNMENT:
1139         if ((obj->owornmask & W_RING) && u_ring) {
1140             ABON(A_CHA)--;
1141             context.botl = 1;
1142         }
1143         break;
1144     case RIN_INCREASE_ACCURACY:
1145         if ((obj->owornmask & W_RING) && u_ring)
1146             u.uhitinc--;
1147         break;
1148     case RIN_INCREASE_DAMAGE:
1149         if ((obj->owornmask & W_RING) && u_ring)
1150             u.udaminc--;
1151         break;
1152     case RIN_PROTECTION:
1153         if (u_ring)
1154             context.botl = 1; /* bot() will recalc u.uac */
1155         break;
1156     case HELM_OF_BRILLIANCE:
1157         if ((obj->owornmask & W_ARMH) && (obj == uarmh)) {
1158             ABON(A_INT)--;
1159             ABON(A_WIS)--;
1160             context.botl = 1;
1161         }
1162         break;
1163     case GAUNTLETS_OF_DEXTERITY:
1164         if ((obj->owornmask & W_ARMG) && (obj == uarmg)) {
1165             ABON(A_DEX)--;
1166             context.botl = 1;
1167         }
1168         break;
1169     default:
1170         break;
1171     }
1172     if (context.botl)
1173         bot();
1174     if (carried(obj))
1175         update_inventory();
1176     return TRUE;
1177 }
1178 
1179 boolean
obj_resists(obj,ochance,achance)1180 obj_resists(obj, ochance, achance)
1181 struct obj *obj;
1182 int ochance, achance; /* percent chance for ordinary objects, artifacts */
1183 {
1184     if (obj->otyp == AMULET_OF_YENDOR
1185         || obj->otyp == SPE_BOOK_OF_THE_DEAD
1186         || obj->otyp == CANDELABRUM_OF_INVOCATION
1187         || obj->otyp == BELL_OF_OPENING
1188         || (obj->otyp == CORPSE && is_rider(&mons[obj->corpsenm]))) {
1189         return TRUE;
1190     } else {
1191         int chance = rn2(100);
1192 
1193         return (boolean) (chance < (obj->oartifact ? achance : ochance));
1194     }
1195 }
1196 
1197 boolean
obj_shudders(obj)1198 obj_shudders(obj)
1199 struct obj *obj;
1200 {
1201     int zap_odds;
1202 
1203     if (context.bypasses && obj->bypass)
1204         return FALSE;
1205 
1206     if (obj->oclass == WAND_CLASS)
1207         zap_odds = 3; /* half-life = 2 zaps */
1208     else if (obj->cursed)
1209         zap_odds = 3; /* half-life = 2 zaps */
1210     else if (obj->blessed)
1211         zap_odds = 12; /* half-life = 8 zaps */
1212     else
1213         zap_odds = 8; /* half-life = 6 zaps */
1214 
1215     /* adjust for "large" quantities of identical things */
1216     if (obj->quan > 4L)
1217         zap_odds /= 2;
1218 
1219     return (boolean) !rn2(zap_odds);
1220 }
1221 
1222 /* Use up at least minwt number of things made of material mat.
1223  * There's also a chance that other stuff will be used up.  Finally,
1224  * there's a random factor here to keep from always using the stuff
1225  * at the top of the pile.
1226  */
1227 STATIC_OVL void
polyuse(objhdr,mat,minwt)1228 polyuse(objhdr, mat, minwt)
1229 struct obj *objhdr;
1230 int mat, minwt;
1231 {
1232     register struct obj *otmp, *otmp2;
1233 
1234     for (otmp = objhdr; minwt > 0 && otmp; otmp = otmp2) {
1235         otmp2 = otmp->nexthere;
1236         if (context.bypasses && otmp->bypass)
1237             continue;
1238         if (otmp == uball || otmp == uchain)
1239             continue;
1240         if (obj_resists(otmp, 0, 0))
1241             continue; /* preserve unique objects */
1242 #ifdef MAIL
1243         if (otmp->otyp == SCR_MAIL)
1244             continue;
1245 #endif
1246 
1247         if (((int) objects[otmp->otyp].oc_material == mat)
1248             == (rn2(minwt + 1) != 0)) {
1249             /* appropriately add damage to bill */
1250             if (costly_spot(otmp->ox, otmp->oy)) {
1251                 if (*u.ushops)
1252                     addtobill(otmp, FALSE, FALSE, FALSE);
1253                 else
1254                     (void) stolen_value(otmp, otmp->ox, otmp->oy, FALSE,
1255                                         FALSE);
1256             }
1257             if (otmp->quan < LARGEST_INT)
1258                 minwt -= (int) otmp->quan;
1259             else
1260                 minwt = 0;
1261             delobj(otmp);
1262         }
1263     }
1264 }
1265 
1266 /*
1267  * Polymorph some of the stuff in this pile into a monster, preferably
1268  * a golem of the kind okind.
1269  */
1270 STATIC_OVL void
create_polymon(obj,okind)1271 create_polymon(obj, okind)
1272 struct obj *obj;
1273 int okind;
1274 {
1275     struct permonst *mdat = (struct permonst *) 0;
1276     struct monst *mtmp;
1277     const char *material;
1278     int pm_index;
1279 
1280     if (context.bypasses) {
1281         /* this is approximate because the "no golems" !obj->nexthere
1282            check below doesn't understand bypassed objects; but it
1283            should suffice since bypassed objects always end up as a
1284            consecutive group at the top of their pile */
1285         while (obj && obj->bypass)
1286             obj = obj->nexthere;
1287     }
1288 
1289     /* no golems if you zap only one object -- not enough stuff */
1290     if (!obj || (!obj->nexthere && obj->quan == 1L))
1291         return;
1292 
1293     /* some of these choices are arbitrary */
1294     switch (okind) {
1295     case IRON:
1296     case METAL:
1297     case MITHRIL:
1298         pm_index = PM_IRON_GOLEM;
1299         material = "metal ";
1300         break;
1301     case COPPER:
1302     case SILVER:
1303     case PLATINUM:
1304     case GEMSTONE:
1305     case MINERAL:
1306         pm_index = rn2(2) ? PM_STONE_GOLEM : PM_CLAY_GOLEM;
1307         material = "lithic ";
1308         break;
1309     case 0:
1310     case FLESH:
1311         /* there is no flesh type, but all food is type 0, so we use it */
1312         pm_index = PM_FLESH_GOLEM;
1313         material = "organic ";
1314         break;
1315     case WOOD:
1316         pm_index = PM_WOOD_GOLEM;
1317         material = "wood ";
1318         break;
1319     case LEATHER:
1320         pm_index = PM_LEATHER_GOLEM;
1321         material = "leather ";
1322         break;
1323     case CLOTH:
1324         pm_index = PM_ROPE_GOLEM;
1325         material = "cloth ";
1326         break;
1327     case BONE:
1328         pm_index = PM_SKELETON; /* nearest thing to "bone golem" */
1329         material = "bony ";
1330         break;
1331     case GOLD:
1332         pm_index = PM_GOLD_GOLEM;
1333         material = "gold ";
1334         break;
1335     case GLASS:
1336         pm_index = PM_GLASS_GOLEM;
1337         material = "glassy ";
1338         break;
1339     case PAPER:
1340         pm_index = PM_PAPER_GOLEM;
1341         material = "paper ";
1342         break;
1343     default:
1344         /* if all else fails... */
1345         pm_index = PM_STRAW_GOLEM;
1346         material = "";
1347         break;
1348     }
1349 
1350     if (!(mvitals[pm_index].mvflags & G_GENOD))
1351         mdat = &mons[pm_index];
1352 
1353     mtmp = makemon(mdat, obj->ox, obj->oy, NO_MM_FLAGS);
1354     polyuse(obj, okind, (int) mons[pm_index].cwt);
1355 
1356     if (mtmp && cansee(mtmp->mx, mtmp->my)) {
1357         pline("Some %sobjects meld, and %s arises from the pile!", material,
1358               a_monnam(mtmp));
1359     }
1360 }
1361 
1362 /* Assumes obj is on the floor. */
1363 void
do_osshock(obj)1364 do_osshock(obj)
1365 struct obj *obj;
1366 {
1367     long i;
1368 
1369 #ifdef MAIL
1370     if (obj->otyp == SCR_MAIL)
1371         return;
1372 #endif
1373     obj_zapped = TRUE;
1374 
1375     if (poly_zapped < 0) {
1376         /* some may metamorphosize */
1377         for (i = obj->quan; i; i--)
1378             if (!rn2(Luck + 45)) {
1379                 poly_zapped = objects[obj->otyp].oc_material;
1380                 break;
1381             }
1382     }
1383 
1384     /* if quan > 1 then some will survive intact */
1385     if (obj->quan > 1L) {
1386         if (obj->quan > LARGEST_INT)
1387             obj = splitobj(obj, (long) rnd(30000));
1388         else
1389             obj = splitobj(obj, (long) rnd((int) obj->quan - 1));
1390     }
1391 
1392     /* appropriately add damage to bill */
1393     if (costly_spot(obj->ox, obj->oy)) {
1394         if (*u.ushops)
1395             addtobill(obj, FALSE, FALSE, FALSE);
1396         else
1397             (void) stolen_value(obj, obj->ox, obj->oy, FALSE, FALSE);
1398     }
1399 
1400     /* zap the object */
1401     delobj(obj);
1402 }
1403 
1404 /* classes of items whose current charge count carries over across polymorph
1405  */
1406 static const char charged_objs[] = { WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS,
1407                                      '\0' };
1408 
1409 /*
1410  * Polymorph the object to the given object ID.  If the ID is STRANGE_OBJECT
1411  * then pick random object from the source's class (this is the standard
1412  * "polymorph" case).  If ID is set to a specific object, inhibit fusing
1413  * n objects into 1.  This could have been added as a flag, but currently
1414  * it is tied to not being the standard polymorph case. The new polymorphed
1415  * object replaces obj in its link chains.  Return value is a pointer to
1416  * the new object.
1417  *
1418  * This should be safe to call for an object anywhere.
1419  */
1420 struct obj *
poly_obj(obj,id)1421 poly_obj(obj, id)
1422 struct obj *obj;
1423 int id;
1424 {
1425     struct obj *otmp;
1426     xchar ox = 0, oy = 0;
1427     long old_wornmask, new_wornmask = 0L;
1428     boolean can_merge = (id == STRANGE_OBJECT);
1429     int obj_location = obj->where;
1430 
1431     if (obj->otyp == BOULDER)
1432         sokoban_guilt();
1433     if (id == STRANGE_OBJECT) { /* preserve symbol */
1434         int try_limit = 3;
1435         unsigned magic_obj = objects[obj->otyp].oc_magic;
1436 
1437         if (obj->otyp == UNICORN_HORN && obj->degraded_horn)
1438             magic_obj = 0;
1439         /* Try up to 3 times to make the magic-or-not status of
1440            the new item be the same as it was for the old one. */
1441         otmp = (struct obj *) 0;
1442         do {
1443             if (otmp)
1444                 delobj(otmp);
1445             otmp = mkobj(obj->oclass, FALSE);
1446         } while (--try_limit > 0
1447                  && objects[otmp->otyp].oc_magic != magic_obj);
1448     } else {
1449         /* literally replace obj with this new thing */
1450         otmp = mksobj(id, FALSE, FALSE);
1451 /* Actually more things use corpsenm but they polymorph differently */
1452 #define USES_CORPSENM(typ) \
1453     ((typ) == CORPSE || (typ) == STATUE || (typ) == FIGURINE)
1454 
1455         if (USES_CORPSENM(obj->otyp) && USES_CORPSENM(id))
1456             set_corpsenm(otmp, obj->corpsenm);
1457 #undef USES_CORPSENM
1458     }
1459 
1460     /* preserve quantity */
1461     otmp->quan = obj->quan;
1462     /* preserve the shopkeepers (lack of) interest */
1463     otmp->no_charge = obj->no_charge;
1464     /* preserve inventory letter if in inventory */
1465     if (obj_location == OBJ_INVENT)
1466         otmp->invlet = obj->invlet;
1467 #ifdef MAIL
1468     /* You can't send yourself 100 mail messages and then
1469      * polymorph them into useful scrolls
1470      */
1471     if (obj->otyp == SCR_MAIL) {
1472         otmp->otyp = SCR_MAIL;
1473         otmp->spe = 1;
1474     }
1475 #endif
1476 
1477     /* avoid abusing eggs laid by you */
1478     if (obj->otyp == EGG && obj->spe) {
1479         int mnum, tryct = 100;
1480 
1481         /* first, turn into a generic egg */
1482         if (otmp->otyp == EGG)
1483             kill_egg(otmp);
1484         else {
1485             otmp->otyp = EGG;
1486             otmp->owt = weight(otmp);
1487         }
1488         otmp->corpsenm = NON_PM;
1489         otmp->spe = 0;
1490 
1491         /* now change it into something laid by the hero */
1492         while (tryct--) {
1493             mnum = can_be_hatched(random_monster(rn2));
1494             if (mnum != NON_PM && !dead_species(mnum, TRUE)) {
1495                 otmp->spe = 1;            /* laid by hero */
1496                 set_corpsenm(otmp, mnum); /* also sets hatch timer */
1497                 break;
1498             }
1499         }
1500     }
1501 
1502     /* keep special fields (including charges on wands) */
1503     if (index(charged_objs, otmp->oclass))
1504         otmp->spe = obj->spe;
1505     otmp->recharged = obj->recharged;
1506 
1507     otmp->cursed = obj->cursed;
1508     otmp->blessed = obj->blessed;
1509 
1510     if (erosion_matters(otmp)) {
1511         if (is_flammable(otmp) || is_rustprone(otmp))
1512             otmp->oeroded = obj->oeroded;
1513         if (is_corrodeable(otmp) || is_rottable(otmp))
1514             otmp->oeroded2 = obj->oeroded2;
1515         if (is_damageable(otmp))
1516             otmp->oerodeproof = obj->oerodeproof;
1517     }
1518 
1519     /* Keep chest/box traps and poisoned ammo if we may */
1520     if (obj->otrapped && Is_box(otmp))
1521         otmp->otrapped = TRUE;
1522 
1523     if (obj->opoisoned && is_poisonable(otmp))
1524         otmp->opoisoned = TRUE;
1525 
1526     if (id == STRANGE_OBJECT && obj->otyp == CORPSE) {
1527         /* turn crocodile corpses into shoes */
1528         if (obj->corpsenm == PM_CROCODILE) {
1529             otmp->otyp = LOW_BOOTS;
1530             otmp->oclass = ARMOR_CLASS;
1531             otmp->spe = 0;
1532             otmp->oeroded = 0;
1533             otmp->oerodeproof = TRUE;
1534             otmp->quan = 1L;
1535             otmp->cursed = FALSE;
1536         }
1537     }
1538 
1539     /* no box contents --KAA */
1540     if (Has_contents(otmp))
1541         delete_contents(otmp);
1542 
1543     /* 'n' merged objects may be fused into 1 object */
1544     if (otmp->quan > 1L && (!objects[otmp->otyp].oc_merge
1545                             || (can_merge && otmp->quan > (long) rn2(1000))))
1546         otmp->quan = 1L;
1547 
1548     switch (otmp->oclass) {
1549     case TOOL_CLASS:
1550         if (otmp->otyp == MAGIC_LAMP) {
1551             otmp->otyp = OIL_LAMP;
1552             otmp->age = 1500L; /* "best" oil lamp possible */
1553         } else if (otmp->otyp == MAGIC_MARKER) {
1554             otmp->recharged = 1; /* degraded quality */
1555         }
1556         /* don't care about the recharge count of other tools */
1557         break;
1558 
1559     case WAND_CLASS:
1560         while (otmp->otyp == WAN_WISHING || otmp->otyp == WAN_POLYMORPH)
1561             otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING);
1562         /* altering the object tends to degrade its quality
1563            (analogous to spellbook `read count' handling) */
1564         if ((int) otmp->recharged < rn2(7)) /* recharge_limit */
1565             otmp->recharged++;
1566         break;
1567 
1568     case POTION_CLASS:
1569         while (otmp->otyp == POT_POLYMORPH)
1570             otmp->otyp = rnd_class(POT_GAIN_ABILITY, POT_WATER);
1571         break;
1572 
1573     case SPBOOK_CLASS:
1574         while (otmp->otyp == SPE_POLYMORPH)
1575             otmp->otyp = rnd_class(SPE_DIG, SPE_BLANK_PAPER);
1576         /* reduce spellbook abuse; non-blank books degrade */
1577         if (otmp->otyp != SPE_BLANK_PAPER) {
1578             otmp->spestudied = obj->spestudied + 1;
1579             if (otmp->spestudied > MAX_SPELL_STUDY) {
1580                 otmp->otyp = SPE_BLANK_PAPER;
1581                 /* writing a new book over it will yield an unstudied
1582                    one; re-polymorphing this one as-is may or may not
1583                    get something non-blank */
1584                 otmp->spestudied = rn2(otmp->spestudied);
1585             }
1586         }
1587         break;
1588 
1589     case GEM_CLASS:
1590         if (otmp->quan > (long) rnd(4)
1591             && objects[obj->otyp].oc_material == MINERAL
1592             && objects[otmp->otyp].oc_material != MINERAL) {
1593             otmp->otyp = ROCK; /* transmutation backfired */
1594             otmp->quan /= 2L;  /* some material has been lost */
1595         }
1596         break;
1597     }
1598 
1599     /* update the weight */
1600     otmp->owt = weight(otmp);
1601 
1602     /*
1603      * ** we are now done adjusting the object (except possibly wearing it) **
1604      */
1605 
1606     (void) get_obj_location(obj, &ox, &oy, BURIED_TOO | CONTAINED_TOO);
1607     old_wornmask = obj->owornmask & ~(W_ART | W_ARTI);
1608     /* swap otmp for obj */
1609     replace_object(obj, otmp);
1610     if (obj_location == OBJ_INVENT) {
1611         /*
1612          * We may need to do extra adjustments for the hero if we're
1613          * messing with the hero's inventory.  The following calls are
1614          * equivalent to calling freeinv on obj and addinv on otmp,
1615          * while doing an in-place swap of the actual objects.
1616          */
1617         freeinv_core(obj);
1618         addinv_core1(otmp);
1619         addinv_core2(otmp);
1620         /*
1621          * Handle polymorph of worn item.  Stone-to-flesh cast on self can
1622          * affect multiple objects at once, but their new forms won't
1623          * produce any side-effects.  A single worn item dipped into potion
1624          * of polymorph can produce side-effects but those won't yield out
1625          * of sequence messages because current polymorph is finished.
1626          */
1627         if (old_wornmask) {
1628             boolean was_twohanded = bimanual(obj), was_twoweap = u.twoweap;
1629 
1630             /* wearslot() returns a mask which might have multiple bits set;
1631                narrow that down to the bit(s) currently in use */
1632             new_wornmask = wearslot(otmp) & old_wornmask;
1633             remove_worn_item(obj, TRUE);
1634             /* if the new form can be worn in the same slot, make it so */
1635             if ((new_wornmask & W_WEP) != 0L) {
1636                 if (was_twohanded || !bimanual(otmp) || !uarms)
1637                     setuwep(otmp);
1638                 if (was_twoweap && uwep && !bimanual(uwep))
1639                     u.twoweap = TRUE;
1640             } else if ((new_wornmask & W_SWAPWEP) != 0L) {
1641                 if (was_twohanded || !bimanual(otmp))
1642                     setuswapwep(otmp);
1643                 if (was_twoweap && uswapwep)
1644                     u.twoweap = TRUE;
1645             } else if ((new_wornmask & W_QUIVER) != 0L) {
1646                 setuqwep(otmp);
1647             } else if (new_wornmask) {
1648                 setworn(otmp, new_wornmask);
1649                 /* set_wear() might result in otmp being destroyed if
1650                    worn amulet has been turned into an amulet of change */
1651                 set_wear(otmp);
1652                 otmp = wearmask_to_obj(new_wornmask); /* might be Null */
1653             }
1654         } /* old_wornmask */
1655     } else if (obj_location == OBJ_FLOOR) {
1656         if (obj->otyp == BOULDER && otmp->otyp != BOULDER
1657             && !does_block(ox, oy, &levl[ox][oy]))
1658             unblock_point(ox, oy);
1659         else if (obj->otyp != BOULDER && otmp->otyp == BOULDER)
1660             /* (checking does_block() here would be redundant) */
1661             block_point(ox, oy);
1662     }
1663 
1664     /* note: if otmp is gone, billing for it was handled by useup() */
1665     if (((otmp && !carried(otmp)) || obj->unpaid) && costly_spot(ox, oy)) {
1666         struct monst *shkp = shop_keeper(*in_rooms(ox, oy, SHOPBASE));
1667 
1668         if ((!obj->no_charge
1669              || (Has_contents(obj)
1670                  && (contained_cost(obj, shkp, 0L, FALSE, FALSE) != 0L)))
1671             && inhishop(shkp)) {
1672             if (shkp->mpeaceful) {
1673                 if (*u.ushops
1674                     && (*in_rooms(u.ux, u.uy, 0)
1675                         == *in_rooms(shkp->mx, shkp->my, 0))
1676                     && !costly_spot(u.ux, u.uy)) {
1677                     make_angry_shk(shkp, ox, oy);
1678                 } else {
1679                     pline("%s gets angry!", Monnam(shkp));
1680                     hot_pursuit(shkp);
1681                 }
1682             } else
1683                 Norep("%s is furious!", Monnam(shkp));
1684         }
1685     }
1686     delobj(obj);
1687     return otmp;
1688 }
1689 
1690 /* stone-to-flesh spell hits and maybe transforms or animates obj */
1691 STATIC_OVL int
stone_to_flesh_obj(obj)1692 stone_to_flesh_obj(obj)
1693 struct obj *obj;
1694 {
1695     int res = 1; /* affected object by default */
1696     struct permonst *ptr;
1697     struct monst *mon, *shkp;
1698     struct obj *item;
1699     xchar oox, ooy;
1700     boolean smell = FALSE, golem_xform = FALSE;
1701 
1702     if (objects[obj->otyp].oc_material != MINERAL
1703         && objects[obj->otyp].oc_material != GEMSTONE)
1704         return 0;
1705     /* Heart of Ahriman usually resists; ordinary items rarely do */
1706     if (obj_resists(obj, 2, 98))
1707         return 0;
1708 
1709     (void) get_obj_location(obj, &oox, &ooy, 0);
1710     /* add more if stone objects are added.. */
1711     switch (objects[obj->otyp].oc_class) {
1712     case ROCK_CLASS: /* boulders and statues */
1713     case TOOL_CLASS: /* figurines */
1714         if (obj->otyp == BOULDER) {
1715             obj = poly_obj(obj, HUGE_CHUNK_OF_MEAT);
1716             smell = TRUE;
1717         } else if (obj->otyp == STATUE || obj->otyp == FIGURINE) {
1718             ptr = &mons[obj->corpsenm];
1719             if (is_golem(ptr)) {
1720                 golem_xform = (ptr != &mons[PM_FLESH_GOLEM]);
1721             } else if (vegetarian(ptr)) {
1722                 /* Don't animate monsters that aren't flesh */
1723                 obj = poly_obj(obj, MEATBALL);
1724                 smell = TRUE;
1725                 break;
1726             }
1727             if (obj->otyp == STATUE) {
1728                 /* animate_statue() forces all golems to become flesh golems */
1729                 mon = animate_statue(obj, oox, ooy, ANIMATE_SPELL, (int *) 0);
1730             } else { /* (obj->otyp == FIGURINE) */
1731                 if (golem_xform)
1732                     ptr = &mons[PM_FLESH_GOLEM];
1733                 mon = makemon(ptr, oox, ooy, NO_MINVENT);
1734                 if (mon) {
1735                     if (costly_spot(oox, ooy)
1736                         && (carried(obj) ? obj->unpaid : !obj->no_charge)) {
1737                         shkp = shop_keeper(*in_rooms(oox, ooy, SHOPBASE));
1738                         stolen_value(obj, oox, ooy,
1739                                      (shkp && shkp->mpeaceful), FALSE);
1740                     }
1741                     if (obj->timed)
1742                         obj_stop_timers(obj);
1743                     if (carried(obj))
1744                         useup(obj);
1745                     else
1746                         delobj(obj);
1747                     if (cansee(mon->mx, mon->my))
1748                         pline_The("figurine %sanimates!",
1749                                   golem_xform ? "turns to flesh and " : "");
1750                 }
1751             }
1752             if (mon) {
1753                 ptr = mon->data;
1754                 /* this golem handling is redundant... */
1755                 if (is_golem(ptr) && ptr != &mons[PM_FLESH_GOLEM])
1756                     (void) newcham(mon, &mons[PM_FLESH_GOLEM], TRUE, FALSE);
1757             } else if ((ptr->geno & (G_NOCORPSE | G_UNIQ)) != 0) {
1758                 /* didn't revive but can't leave corpse either */
1759                 res = 0;
1760             } else {
1761                 /* unlikely to get here since genociding monsters also
1762                    sets the G_NOCORPSE flag; drop statue's contents */
1763                 while ((item = obj->cobj) != 0) {
1764                     bypass_obj(item); /* make stone-to-flesh miss it */
1765                     obj_extract_self(item);
1766                     place_object(item, oox, ooy);
1767                 }
1768                 obj = poly_obj(obj, CORPSE);
1769             }
1770         } else { /* miscellaneous tool or unexpected rock... */
1771             res = 0;
1772         }
1773         break;
1774     /* maybe add weird things to become? */
1775     case RING_CLASS: /* some of the rings are stone */
1776         obj = poly_obj(obj, MEAT_RING);
1777         smell = TRUE;
1778         break;
1779     case WAND_CLASS: /* marble wand */
1780         obj = poly_obj(obj, MEAT_STICK);
1781         smell = TRUE;
1782         break;
1783     case GEM_CLASS: /* stones & gems */
1784         obj = poly_obj(obj, MEATBALL);
1785         smell = TRUE;
1786         break;
1787     case WEAPON_CLASS: /* crysknife */
1788         /*FALLTHRU*/
1789     default:
1790         res = 0;
1791         break;
1792     }
1793 
1794     if (smell) {
1795         /* non-meat eaters smell meat, meat eaters smell its flavor;
1796            monks are considered non-meat eaters regardless of behavior;
1797            other roles are non-meat eaters if they haven't broken
1798            vegetarian conduct yet (or if poly'd into non-carnivorous/
1799            non-omnivorous form, regardless of whether it's herbivorous,
1800            non-eating, or something stranger) */
1801         if (Role_if(PM_MONK) || !u.uconduct.unvegetarian
1802             || !carnivorous(youmonst.data))
1803             Norep("You smell the odor of meat.");
1804         else
1805             Norep("You smell a delicious smell.");
1806     }
1807     newsym(oox, ooy);
1808     return res;
1809 }
1810 
1811 /*
1812  * Object obj was hit by the effect of the wand/spell otmp.  Return
1813  * non-zero if the wand/spell had any effect.
1814  */
1815 int
bhito(obj,otmp)1816 bhito(obj, otmp)
1817 struct obj *obj, *otmp;
1818 {
1819     int res = 1; /* affected object by default */
1820     boolean learn_it = FALSE, maybelearnit;
1821 
1822     /* fundamental: a wand effect hitting itself doesn't do anything;
1823        otherwise we need to guard against accessing otmp after something
1824        strange has happened to it (along the lines of polymorph or
1825        stone-to-flesh [which aren't good examples since polymorph wands
1826        aren't affected by polymorph zaps and stone-to-flesh isn't
1827        available in wand form, but the concept still applies...]) */
1828     if (obj == otmp)
1829         return 0;
1830 
1831     if (obj->bypass) {
1832         /* The bypass bit is currently only used as follows:
1833          *
1834          * POLYMORPH - When a monster being polymorphed drops something
1835          *             from its inventory as a result of the change.
1836          *             If the items fall to the floor, they are not
1837          *             subject to direct subsequent polymorphing
1838          *             themselves on that same zap.  This makes it
1839          *             consistent with items that remain in the monster's
1840          *             inventory.  They are not polymorphed either.
1841          * UNDEAD_TURNING - When an undead creature gets killed via
1842          *             undead turning, prevent its corpse from being
1843          *             immediately revived by the same effect.
1844          * STONE_TO_FLESH - If a statue can't be revived, its
1845          *             contents get dropped before turning it into
1846          *             meat; prevent those contents from being hit.
1847          * retouch_equipment() - bypass flag is used to track which
1848          *             items have been handled (bhito isn't involved).
1849          * menu_drop(), askchain() - inventory traversal where multiple
1850          *             Drop can alter the invent chain while traversal
1851          *             is in progress (bhito isn't involved).
1852          * destroy_item(), destroy_mitem() - inventory traversal where
1853          *             item destruction can trigger drop or destruction of
1854          *             other item(s) and alter the invent or mon->minvent
1855          *             chain, possibly recursively.
1856          *
1857          * The bypass bit on all objects is reset each turn, whenever
1858          * context.bypasses is set.
1859          *
1860          * We check the obj->bypass bit above AND context.bypasses
1861          * as a safeguard against any stray occurrence left in an obj
1862          * struct someplace, although that should never happen.
1863          */
1864         if (context.bypasses) {
1865             return 0;
1866         } else {
1867             debugpline1("%s for a moment.", Tobjnam(obj, "pulsate"));
1868             obj->bypass = 0;
1869         }
1870     }
1871 
1872     /*
1873      * Some parts of this function expect the object to be on the floor
1874      * obj->{ox,oy} to be valid.  The exception to this (so far) is
1875      * for the STONE_TO_FLESH spell.
1876      */
1877     if (!(obj->where == OBJ_FLOOR || otmp->otyp == SPE_STONE_TO_FLESH))
1878         impossible("bhito: obj is not floor or Stone To Flesh spell");
1879 
1880     if (obj == uball) {
1881         res = 0;
1882     } else if (obj == uchain) {
1883         if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) {
1884             learn_it = TRUE;
1885             unpunish();
1886         } else
1887             res = 0;
1888     } else
1889         switch (otmp->otyp) {
1890         case WAN_POLYMORPH:
1891         case SPE_POLYMORPH:
1892             if (obj->otyp == WAN_POLYMORPH || obj->otyp == SPE_POLYMORPH
1893                 || obj->otyp == POT_POLYMORPH || obj_resists(obj, 5, 95)) {
1894                 res = 0;
1895                 break;
1896             }
1897             /* KMH, conduct */
1898             u.uconduct.polypiles++;
1899             /* any saved lock context will be dangerously obsolete */
1900             if (Is_box(obj))
1901                 (void) boxlock(obj, otmp);
1902 
1903             if (obj_shudders(obj)) {
1904                 boolean cover = ((obj == level.objects[u.ux][u.uy])
1905                                  && u.uundetected
1906                                  && hides_under(youmonst.data));
1907 
1908                 if (cansee(obj->ox, obj->oy))
1909                     learn_it = TRUE;
1910                 do_osshock(obj);
1911                 /* eek - your cover might have been blown */
1912                 if (cover)
1913                     (void) hideunder(&youmonst);
1914                 break;
1915             }
1916             obj = poly_obj(obj, STRANGE_OBJECT);
1917             newsym(obj->ox, obj->oy);
1918             break;
1919         case WAN_PROBING:
1920             res = !obj->dknown;
1921             /* target object has now been "seen (up close)" */
1922             obj->dknown = 1;
1923             if (Is_container(obj) || obj->otyp == STATUE) {
1924                 obj->cknown = obj->lknown = 1;
1925                 if (!obj->cobj) {
1926                     pline("%s empty.", Tobjnam(obj, "are"));
1927                 } else if (SchroedingersBox(obj)) {
1928                     /* we don't want to force alive vs dead
1929                        determination for Schroedinger's Cat here,
1930                        so just make probing be inconclusive for it */
1931                     You("aren't sure whether %s has %s or its corpse inside.",
1932                         the(xname(obj)),
1933                         /* unfortunately, we can't tell whether rndmonnam()
1934                            picks a form which can't leave a corpse */
1935                         an(Hallucination ? rndmonnam((char *) 0) : "cat"));
1936                     obj->cknown = 0;
1937                 } else {
1938                     struct obj *o;
1939 
1940                     /* view contents (not recursively) */
1941                     for (o = obj->cobj; o; o = o->nobj)
1942                         o->dknown = 1; /* "seen", even if blind */
1943                     (void) display_cinventory(obj);
1944                 }
1945                 res = 1;
1946             }
1947             if (res)
1948                 learn_it = TRUE;
1949             break;
1950         case WAN_STRIKING:
1951         case SPE_FORCE_BOLT:
1952             /* learn the type if you see or hear something break
1953                (the sound could be implicit) */
1954             maybelearnit = cansee(obj->ox, obj->oy) || !Deaf;
1955             if (obj->otyp == BOULDER) {
1956                 if (cansee(obj->ox, obj->oy))
1957                     pline_The("boulder falls apart.");
1958                 else
1959                     You_hear("a crumbling sound.");
1960                 fracture_rock(obj);
1961             } else if (obj->otyp == STATUE) {
1962                 if (break_statue(obj)) {
1963                     if (cansee(obj->ox, obj->oy)) {
1964                         if (Hallucination)
1965                             pline_The("%s shatters.", rndmonnam(NULL));
1966                         else
1967                             pline_The("statue shatters.");
1968                     } else
1969                         You_hear("a crumbling sound.");
1970                 }
1971             } else {
1972                 int oox = obj->ox;
1973                 int ooy = obj->oy;
1974                 if (context.mon_moving
1975                         ? !breaks(obj, obj->ox, obj->oy)
1976                         : !hero_breaks(obj, obj->ox, obj->oy, FALSE))
1977                     maybelearnit = FALSE; /* nothing broke */
1978                 else
1979                     newsym_force(oox,ooy);
1980                 res = 0;
1981             }
1982             if (maybelearnit)
1983                 learn_it = TRUE;
1984             break;
1985         case WAN_CANCELLATION:
1986         case SPE_CANCELLATION:
1987             cancel_item(obj);
1988 #ifdef TEXTCOLOR
1989             newsym(obj->ox, obj->oy); /* might change color */
1990 #endif
1991             break;
1992         case SPE_DRAIN_LIFE:
1993             (void) drain_item(obj, TRUE);
1994             break;
1995         case WAN_TELEPORTATION:
1996         case SPE_TELEPORT_AWAY:
1997             (void) rloco(obj);
1998             break;
1999         case WAN_MAKE_INVISIBLE:
2000             break;
2001         case WAN_UNDEAD_TURNING:
2002         case SPE_TURN_UNDEAD:
2003             if (obj->otyp == EGG) {
2004                 revive_egg(obj);
2005             } else if (obj->otyp == CORPSE) {
2006                 struct monst *mtmp;
2007                 xchar ox, oy;
2008                 int corpsenm = corpse_revive_type(obj);
2009                 char *corpsname = cxname_singular(obj);
2010 
2011                 /* get corpse's location before revive() uses it up */
2012                 if (!get_obj_location(obj, &ox, &oy, 0))
2013                     ox = obj->ox, oy = obj->oy; /* won't happen */
2014 
2015                 mtmp = revive(obj, TRUE);
2016                 if (!mtmp) {
2017                     res = 0; /* no monster implies corpse was left intact */
2018                 } else {
2019                     if (cansee(ox, oy)) {
2020                         if (canspotmon(mtmp)) {
2021                             pline("%s is resurrected!",
2022                                   upstart(noname_monnam(mtmp, ARTICLE_THE)));
2023                             learn_it = TRUE;
2024                         } else {
2025                             /* saw corpse but don't see monster: maybe
2026                                mtmp is invisible, or has been placed at
2027                                a different spot than <ox,oy> */
2028                             if (!type_is_pname(&mons[corpsenm]))
2029                                 corpsname = The(corpsname);
2030                             pline("%s disappears.", corpsname);
2031                         }
2032                     } else {
2033                         /* couldn't see corpse's location */
2034                         if (Role_if(PM_HEALER) && !Deaf
2035                             && !nonliving(&mons[corpsenm])) {
2036                             if (!type_is_pname(&mons[corpsenm]))
2037                                 corpsname = an(corpsname);
2038                             if (!Hallucination)
2039                                 You_hear("%s reviving.", corpsname);
2040                             else
2041                                 You_hear("a defibrillator.");
2042                             learn_it = TRUE;
2043                         }
2044                         if (canspotmon(mtmp))
2045                             /* didn't see corpse but do see monster: it
2046                                has been placed somewhere other than <ox,oy>
2047                                or blind hero spots it with ESP */
2048                             pline("%s appears.", Monnam(mtmp));
2049                     }
2050                     if (learn_it)
2051                         exercise(A_WIS, TRUE);
2052                 }
2053             }
2054             break;
2055         case WAN_OPENING:
2056         case SPE_KNOCK:
2057         case WAN_LOCKING:
2058         case SPE_WIZARD_LOCK:
2059             if (Is_box(obj))
2060                 res = boxlock(obj, otmp);
2061             else
2062                 res = 0;
2063             if (res)
2064                 learn_it = TRUE;
2065             break;
2066         case WAN_SLOW_MONSTER: /* no effect on objects */
2067         case SPE_SLOW_MONSTER:
2068         case WAN_SPEED_MONSTER:
2069         case WAN_NOTHING:
2070         case SPE_HEALING:
2071         case SPE_EXTRA_HEALING:
2072             res = 0;
2073             break;
2074         case SPE_STONE_TO_FLESH:
2075             res = stone_to_flesh_obj(obj);
2076             break;
2077         default:
2078             impossible("What an interesting effect (%d)", otmp->otyp);
2079             break;
2080         }
2081     /* if effect was observable then discover the wand type provided
2082        that the wand itself has been seen */
2083     if (learn_it)
2084         learnwand(otmp);
2085     return res;
2086 }
2087 
2088 /* returns nonzero if something was hit */
2089 int
bhitpile(obj,fhito,tx,ty,zz)2090 bhitpile(obj, fhito, tx, ty, zz)
2091 struct obj *obj;
2092 int FDECL((*fhito), (OBJ_P, OBJ_P));
2093 int tx, ty;
2094 schar zz;
2095 {
2096     int hitanything = 0;
2097     register struct obj *otmp, *next_obj;
2098 
2099     if (obj->otyp == SPE_FORCE_BOLT || obj->otyp == WAN_STRIKING) {
2100         struct trap *t = t_at(tx, ty);
2101 
2102         /* We can't settle for the default calling sequence of
2103            bhito(otmp) -> break_statue(otmp) -> activate_statue_trap(ox,oy)
2104            because that last call might end up operating on our `next_obj'
2105            (below), rather than on the current object, if it happens to
2106            encounter a statue which mustn't become animated. */
2107         if (t && t->ttyp == STATUE_TRAP
2108             && activate_statue_trap(t, tx, ty, TRUE))
2109             learnwand(obj);
2110     }
2111 
2112     poly_zapped = -1;
2113     for (otmp = level.objects[tx][ty]; otmp; otmp = next_obj) {
2114         next_obj = otmp->nexthere;
2115         /* for zap downwards, don't hit object poly'd hero is hiding under */
2116         if (zz > 0 && u.uundetected && otmp == level.objects[u.ux][u.uy]
2117             && hides_under(youmonst.data))
2118             continue;
2119 
2120         hitanything += (*fhito)(otmp, obj);
2121     }
2122     if (poly_zapped >= 0)
2123         create_polymon(level.objects[tx][ty], poly_zapped);
2124 
2125     return hitanything;
2126 }
2127 
2128 /*
2129  * zappable - returns 1 if zap is available, 0 otherwise.
2130  *            it removes a charge from the wand if zappable.
2131  * added by GAN 11/03/86
2132  */
2133 int
zappable(wand)2134 zappable(wand)
2135 register struct obj *wand;
2136 {
2137     if (wand->spe < 0 || (wand->spe == 0 && rn2(121)))
2138         return 0;
2139     if (wand->spe == 0)
2140         You("wrest one last charge from the worn-out wand.");
2141     wand->spe--;
2142     return 1;
2143 }
2144 
2145 /*
2146  * zapnodir - zaps a NODIR wand/spell.
2147  * added by GAN 11/03/86
2148  */
2149 void
zapnodir(obj)2150 zapnodir(obj)
2151 register struct obj *obj;
2152 {
2153     boolean known = FALSE;
2154 
2155     switch (obj->otyp) {
2156     case WAN_LIGHT:
2157     case SPE_LIGHT:
2158         litroom(TRUE, obj);
2159         if (!Blind)
2160             known = TRUE;
2161         if (lightdamage(obj, TRUE, 5))
2162             known = TRUE;
2163         break;
2164     case WAN_SECRET_DOOR_DETECTION:
2165     case SPE_DETECT_UNSEEN:
2166         if (!findit())
2167             return;
2168         if (!Blind)
2169             known = TRUE;
2170         break;
2171     case WAN_CREATE_MONSTER:
2172         known = create_critters(rn2(23) ? 1 : rn1(7, 2),
2173                                 (struct permonst *) 0, FALSE);
2174         break;
2175     case WAN_WISHING:
2176         known = TRUE;
2177         if (Luck + rn2(5) < 0) {
2178             pline("Unfortunately, nothing happens.");
2179             break;
2180         }
2181         makewish();
2182         break;
2183     case WAN_ENLIGHTENMENT:
2184         known = TRUE;
2185         You_feel("self-knowledgeable...");
2186         display_nhwindow(WIN_MESSAGE, FALSE);
2187         enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS);
2188         pline_The("feeling subsides.");
2189         exercise(A_WIS, TRUE);
2190         break;
2191     }
2192     if (known) {
2193         if (!objects[obj->otyp].oc_name_known)
2194             more_experienced(0, 10);
2195         /* effect was observable; discover the wand type provided
2196            that the wand itself has been seen */
2197         learnwand(obj);
2198     }
2199 }
2200 
2201 STATIC_OVL void
backfire(otmp)2202 backfire(otmp)
2203 struct obj *otmp;
2204 {
2205     int dmg;
2206 
2207     otmp->in_use = TRUE; /* in case losehp() is fatal */
2208     pline("%s suddenly explodes!", The(xname(otmp)));
2209     dmg = d(otmp->spe + 2, 6);
2210     losehp(Maybe_Half_Phys(dmg), "exploding wand", KILLED_BY_AN);
2211     useup(otmp);
2212 }
2213 
2214 static NEARDATA const char zap_syms[] = { WAND_CLASS, 0 };
2215 
2216 /* 'z' command (or 'y' if numbed_pad==-1) */
2217 int
dozap()2218 dozap()
2219 {
2220     register struct obj *obj;
2221     int damage;
2222 
2223     if (check_capacity((char *) 0))
2224         return 0;
2225     obj = getobj(zap_syms, "zap");
2226     if (!obj)
2227         return 0;
2228 
2229     check_unpaid(obj);
2230 
2231     /* zappable addition done by GAN 11/03/86 */
2232     if (!zappable(obj))
2233         pline1(nothing_happens);
2234     else if (obj->cursed && !rn2(WAND_BACKFIRE_CHANCE)) {
2235         backfire(obj); /* the wand blows up in your face! */
2236         exercise(A_STR, FALSE);
2237         return 1;
2238     } else if (!(objects[obj->otyp].oc_dir == NODIR) && !getdir((char *) 0)) {
2239         if (!Blind)
2240             pline("%s glows and fades.", The(xname(obj)));
2241         /* make him pay for knowing !NODIR */
2242     } else if (!u.dx && !u.dy && !u.dz
2243                && !(objects[obj->otyp].oc_dir == NODIR)) {
2244         if ((damage = zapyourself(obj, TRUE)) != 0) {
2245             char buf[BUFSZ];
2246 
2247             Sprintf(buf, "zapped %sself with a wand", uhim());
2248             losehp(Maybe_Half_Phys(damage), buf, NO_KILLER_PREFIX);
2249         }
2250     } else {
2251         /*      Are we having fun yet?
2252          * weffects -> buzz(obj->otyp) -> zhitm (temple priest) ->
2253          * attack -> hitum -> known_hitum -> ghod_hitsu ->
2254          * buzz(AD_ELEC) -> destroy_item(WAND_CLASS) ->
2255          * useup -> obfree -> dealloc_obj -> free(obj)
2256          */
2257         current_wand = obj;
2258         weffects(obj);
2259         obj = current_wand;
2260         current_wand = 0;
2261     }
2262     if (obj && obj->spe < 0) {
2263         pline("%s to dust.", Tobjnam(obj, "turn"));
2264         useup(obj);
2265     }
2266     update_inventory(); /* maybe used a charge */
2267     return 1;
2268 }
2269 
2270 int
zapyourself(obj,ordinary)2271 zapyourself(obj, ordinary)
2272 struct obj *obj;
2273 boolean ordinary;
2274 {
2275     boolean learn_it = FALSE;
2276     int damage = 0;
2277 
2278     switch (obj->otyp) {
2279     case WAN_STRIKING:
2280     case SPE_FORCE_BOLT:
2281         learn_it = TRUE;
2282         if (Antimagic) {
2283             shieldeff(u.ux, u.uy);
2284             pline("Boing!");
2285         } else {
2286             if (ordinary) {
2287                 You("bash yourself!");
2288                 damage = d(2, 12);
2289             } else
2290                 damage = d(1 + obj->spe, 6);
2291             exercise(A_STR, FALSE);
2292         }
2293         break;
2294 
2295     case WAN_LIGHTNING:
2296         learn_it = TRUE;
2297         if (!Shock_resistance) {
2298             You("shock yourself!");
2299             damage = d(12, 6);
2300             exercise(A_CON, FALSE);
2301         } else {
2302             shieldeff(u.ux, u.uy);
2303             You("zap yourself, but seem unharmed.");
2304             ugolemeffects(AD_ELEC, d(12, 6));
2305         }
2306         destroy_item(WAND_CLASS, AD_ELEC);
2307         destroy_item(RING_CLASS, AD_ELEC);
2308         (void) flashburn((long) rnd(100));
2309         break;
2310 
2311     case SPE_FIREBALL:
2312         You("explode a fireball on top of yourself!");
2313         explode(u.ux, u.uy, 11, d(6, 6), WAND_CLASS, EXPL_FIERY);
2314         break;
2315     case WAN_FIRE:
2316     case FIRE_HORN:
2317         learn_it = TRUE;
2318         if (Fire_resistance) {
2319             shieldeff(u.ux, u.uy);
2320             You_feel("rather warm.");
2321             ugolemeffects(AD_FIRE, d(12, 6));
2322         } else {
2323             pline("You've set yourself afire!");
2324             damage = d(12, 6);
2325         }
2326         burn_away_slime();
2327         (void) burnarmor(&youmonst);
2328         destroy_item(SCROLL_CLASS, AD_FIRE);
2329         destroy_item(POTION_CLASS, AD_FIRE);
2330         destroy_item(SPBOOK_CLASS, AD_FIRE);
2331         destroy_item(FOOD_CLASS, AD_FIRE); /* only slime for now */
2332         break;
2333 
2334     case WAN_COLD:
2335     case SPE_CONE_OF_COLD:
2336     case FROST_HORN:
2337         learn_it = TRUE;
2338         if (Cold_resistance) {
2339             shieldeff(u.ux, u.uy);
2340             You_feel("a little chill.");
2341             ugolemeffects(AD_COLD, d(12, 6));
2342         } else {
2343             You("imitate a popsicle!");
2344             damage = d(12, 6);
2345         }
2346         destroy_item(POTION_CLASS, AD_COLD);
2347         break;
2348 
2349     case WAN_MAGIC_MISSILE:
2350     case SPE_MAGIC_MISSILE:
2351         learn_it = TRUE;
2352         if (Antimagic) {
2353             shieldeff(u.ux, u.uy);
2354             pline_The("missiles bounce!");
2355         } else {
2356             damage = d(4, 6);
2357             pline("Idiot!  You've shot yourself!");
2358         }
2359         break;
2360 
2361     case WAN_POLYMORPH:
2362     case SPE_POLYMORPH:
2363         if (!Unchanging) {
2364             learn_it = TRUE;
2365             polyself(0);
2366         }
2367         break;
2368 
2369     case WAN_CANCELLATION:
2370     case SPE_CANCELLATION:
2371         (void) cancel_monst(&youmonst, obj, TRUE, TRUE, TRUE);
2372         break;
2373 
2374     case SPE_DRAIN_LIFE:
2375         if (!Drain_resistance) {
2376             learn_it = TRUE; /* (no effect for spells...) */
2377             losexp("life drainage");
2378         }
2379         damage = 0; /* No additional damage */
2380         break;
2381 
2382     case WAN_MAKE_INVISIBLE: {
2383         /* have to test before changing HInvis but must change
2384          * HInvis before doing newsym().
2385          */
2386         int msg = !Invis && !Blind && !BInvis;
2387 
2388         if (BInvis && uarmc->otyp == MUMMY_WRAPPING) {
2389             /* A mummy wrapping absorbs it and protects you */
2390             You_feel("rather itchy under %s.", yname(uarmc));
2391             break;
2392         }
2393         if (ordinary || !rn2(10)) { /* permanent */
2394             HInvis |= FROMOUTSIDE;
2395         } else { /* temporary */
2396             incr_itimeout(&HInvis, d(obj->spe, 250));
2397         }
2398         if (msg) {
2399             learn_it = TRUE;
2400             newsym(u.ux, u.uy);
2401             self_invis_message();
2402         }
2403         break;
2404     }
2405 
2406     case WAN_SPEED_MONSTER:
2407         if (!(HFast & INTRINSIC)) {
2408             learn_it = TRUE;
2409             if (!Fast)
2410                 You("speed up.");
2411             else
2412                 Your("quickness feels more natural.");
2413             exercise(A_DEX, TRUE);
2414         }
2415         HFast |= FROMOUTSIDE;
2416         break;
2417 
2418     case WAN_SLEEP:
2419     case SPE_SLEEP:
2420         learn_it = TRUE;
2421         if (Sleep_resistance) {
2422             shieldeff(u.ux, u.uy);
2423             You("don't feel sleepy!");
2424         } else {
2425             pline_The("sleep ray hits you!");
2426             fall_asleep(-rnd(50), TRUE);
2427         }
2428         break;
2429 
2430     case WAN_SLOW_MONSTER:
2431     case SPE_SLOW_MONSTER:
2432         if (HFast & (TIMEOUT | INTRINSIC)) {
2433             learn_it = TRUE;
2434             u_slow_down();
2435         }
2436         break;
2437 
2438     case WAN_TELEPORTATION:
2439     case SPE_TELEPORT_AWAY:
2440         tele();
2441         /* same criteria as when mounted (zap_steed) */
2442         if ((Teleport_control && !Stunned) || !couldsee(u.ux0, u.uy0)
2443             || distu(u.ux0, u.uy0) >= 16)
2444             learn_it = TRUE;
2445         break;
2446 
2447     case WAN_DEATH:
2448     case SPE_FINGER_OF_DEATH:
2449         if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
2450             pline((obj->otyp == WAN_DEATH)
2451                       ? "The wand shoots an apparently harmless beam at you."
2452                       : "You seem no deader than before.");
2453             break;
2454         }
2455         learn_it = TRUE;
2456         Sprintf(killer.name, "shot %sself with a death ray", uhim());
2457         killer.format = NO_KILLER_PREFIX;
2458         You("irradiate yourself with pure energy!");
2459         You("die.");
2460         /* They might survive with an amulet of life saving */
2461         done(DIED);
2462         break;
2463     case WAN_UNDEAD_TURNING:
2464     case SPE_TURN_UNDEAD:
2465         learn_it = TRUE;
2466         (void) unturn_dead(&youmonst);
2467         if (is_undead(youmonst.data)) {
2468             You_feel("frightened and %sstunned.",
2469                      Stunned ? "even more " : "");
2470             make_stunned((HStun & TIMEOUT) + (long) rnd(30), FALSE);
2471         } else
2472             You("shudder in dread.");
2473         break;
2474     case SPE_HEALING:
2475     case SPE_EXTRA_HEALING:
2476         learn_it = TRUE; /* (no effect for spells...) */
2477         healup(d(6, obj->otyp == SPE_EXTRA_HEALING ? 8 : 4), 0, FALSE,
2478                (obj->blessed || obj->otyp == SPE_EXTRA_HEALING));
2479         You_feel("%sbetter.", obj->otyp == SPE_EXTRA_HEALING ? "much " : "");
2480         break;
2481     case WAN_LIGHT: /* (broken wand) */
2482         /* assert( !ordinary ); */
2483         damage = d(obj->spe, 25);
2484         /*FALLTHRU*/
2485     case EXPENSIVE_CAMERA:
2486         if (!damage)
2487             damage = 5;
2488         damage = lightdamage(obj, ordinary, damage);
2489         damage += rnd(25);
2490         if (flashburn((long) damage))
2491             learn_it = TRUE;
2492         damage = 0; /* reset */
2493         break;
2494     case WAN_OPENING:
2495     case SPE_KNOCK:
2496         if (Punished) {
2497             learn_it = TRUE;
2498             unpunish();
2499         }
2500         /* invent is hit iff hero doesn't escape from a trap */
2501         if (!u.utrap || !openholdingtrap(&youmonst, &learn_it)) {
2502             struct obj *otmp;
2503             boolean boxing = FALSE;
2504 
2505             /* unlock carried boxes */
2506             for (otmp = invent; otmp; otmp = otmp->nobj)
2507                 if (Is_box(otmp)) {
2508                     (void) boxlock(otmp, obj);
2509                     boxing = TRUE;
2510                 }
2511             if (boxing)
2512                 update_inventory(); /* in case any box->lknown has changed */
2513 
2514             /* trigger previously escaped trapdoor */
2515             (void) openfallingtrap(&youmonst, TRUE, &learn_it);
2516         }
2517         break;
2518     case WAN_LOCKING:
2519     case SPE_WIZARD_LOCK:
2520         /* similar logic to opening; invent is hit iff no trap triggered */
2521         if (u.utrap || !closeholdingtrap(&youmonst, &learn_it)) {
2522             struct obj *otmp;
2523             boolean boxing = FALSE;
2524 
2525             /* lock carried boxes */
2526             for (otmp = invent; otmp; otmp = otmp->nobj)
2527                 if (Is_box(otmp)) {
2528                     (void) boxlock(otmp, obj);
2529                     boxing = TRUE;
2530                 }
2531             if (boxing)
2532                 update_inventory(); /* in case any box->lknown has changed */
2533         }
2534         break;
2535     case WAN_DIGGING:
2536     case SPE_DIG:
2537     case SPE_DETECT_UNSEEN:
2538     case WAN_NOTHING:
2539         break;
2540     case WAN_PROBING: {
2541         struct obj *otmp;
2542 
2543         for (otmp = invent; otmp; otmp = otmp->nobj) {
2544             otmp->dknown = 1;
2545             if (Is_container(otmp) || otmp->otyp == STATUE) {
2546                 otmp->lknown = 1;
2547                 if (!SchroedingersBox(otmp))
2548                     otmp->cknown = 1;
2549             }
2550         }
2551         update_inventory();
2552         learn_it = TRUE;
2553         ustatusline();
2554         break;
2555     }
2556     case SPE_STONE_TO_FLESH: {
2557         struct obj *otmp, *onxt;
2558         boolean didmerge;
2559 
2560         if (u.umonnum == PM_STONE_GOLEM) {
2561             learn_it = TRUE;
2562             (void) polymon(PM_FLESH_GOLEM);
2563         }
2564         if (Stoned) {
2565             learn_it = TRUE;
2566             fix_petrification(); /* saved! */
2567         }
2568         /* but at a cost.. */
2569         for (otmp = invent; otmp; otmp = onxt) {
2570             onxt = otmp->nobj;
2571             if (bhito(otmp, obj))
2572                 learn_it = TRUE;
2573         }
2574         /*
2575          * It is possible that we can now merge some inventory.
2576          * Do a highly paranoid merge.  Restart from the beginning
2577          * until no merges.
2578          */
2579         do {
2580             didmerge = FALSE;
2581             for (otmp = invent; !didmerge && otmp; otmp = otmp->nobj)
2582                 for (onxt = otmp->nobj; onxt; onxt = onxt->nobj)
2583                     if (merged(&otmp, &onxt)) {
2584                         didmerge = TRUE;
2585                         break;
2586                     }
2587         } while (didmerge);
2588         break;
2589     }
2590     default:
2591         impossible("zapyourself: object %d used?", obj->otyp);
2592         break;
2593     }
2594     /* if effect was observable then discover the wand type provided
2595        that the wand itself has been seen */
2596     if (learn_it)
2597         learnwand(obj);
2598     return damage;
2599 }
2600 
2601 /* called when poly'd hero uses breath attack against self */
2602 void
ubreatheu(mattk)2603 ubreatheu(mattk)
2604 struct attack *mattk;
2605 {
2606     int dtyp = 20 + mattk->adtyp - 1;      /* breath by hero */
2607     const char *fltxt = flash_types[dtyp]; /* blast of <something> */
2608 
2609     zhitu(dtyp, mattk->damn, fltxt, u.ux, u.uy);
2610 }
2611 
2612 /* light damages hero in gremlin form */
2613 int
lightdamage(obj,ordinary,amt)2614 lightdamage(obj, ordinary, amt)
2615 struct obj *obj;  /* item making light (fake book if spell) */
2616 boolean ordinary; /* wand/camera zap vs wand destruction */
2617 int amt;          /* pseudo-damage used to determine blindness duration */
2618 {
2619     char buf[BUFSZ];
2620     const char *how;
2621     int dmg = amt;
2622 
2623     if (dmg && youmonst.data == &mons[PM_GREMLIN]) {
2624         /* reduce high values (from destruction of wand with many charges) */
2625         dmg = rnd(dmg);
2626         if (dmg > 10)
2627             dmg = 10 + rnd(dmg - 10);
2628         if (dmg > 20)
2629             dmg = 20;
2630         pline("Ow, that light hurts%c", (dmg > 2 || u.mh <= 5) ? '!' : '.');
2631         /* [composing killer/reason is superfluous here; if fatal, cause
2632            of death will always be "killed while stuck in creature form"] */
2633         if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS)
2634             ordinary = FALSE; /* say blasted rather than zapped */
2635         how = (obj->oclass != SPBOOK_CLASS)
2636                   ? (const char *) ansimpleoname(obj)
2637                   : "spell of light";
2638         Sprintf(buf, "%s %sself with %s", ordinary ? "zapped" : "blasted",
2639                 uhim(), how);
2640         /* might rehumanize(); could be fatal, but only for Unchanging */
2641         losehp(Maybe_Half_Phys(dmg), buf, NO_KILLER_PREFIX);
2642     }
2643     return dmg;
2644 }
2645 
2646 /* light[ning] causes blindness */
2647 boolean
flashburn(duration)2648 flashburn(duration)
2649 long duration;
2650 {
2651     if (!resists_blnd(&youmonst)) {
2652         You(are_blinded_by_the_flash);
2653         make_blinded(duration, FALSE);
2654         if (!Blind)
2655             Your1(vision_clears);
2656         return TRUE;
2657     }
2658     return FALSE;
2659 }
2660 
2661 /* you've zapped a wand downwards while riding
2662  * Return TRUE if the steed was hit by the wand.
2663  * Return FALSE if the steed was not hit by the wand.
2664  */
2665 STATIC_OVL boolean
zap_steed(obj)2666 zap_steed(obj)
2667 struct obj *obj; /* wand or spell */
2668 {
2669     int steedhit = FALSE;
2670 
2671     bhitpos.x = u.usteed->mx, bhitpos.y = u.usteed->my;
2672     notonhead = FALSE;
2673     switch (obj->otyp) {
2674     /*
2675      * Wands that are allowed to hit the steed
2676      * Carefully test the results of any that are
2677      * moved here from the bottom section.
2678      */
2679     case WAN_PROBING:
2680         probe_monster(u.usteed);
2681         learnwand(obj);
2682         steedhit = TRUE;
2683         break;
2684     case WAN_TELEPORTATION:
2685     case SPE_TELEPORT_AWAY:
2686         /* you go together */
2687         tele();
2688         /* same criteria as when unmounted (zapyourself) */
2689         if ((Teleport_control && !Stunned) || !couldsee(u.ux0, u.uy0)
2690             || distu(u.ux0, u.uy0) >= 16)
2691             learnwand(obj);
2692         steedhit = TRUE;
2693         break;
2694 
2695     /* Default processing via bhitm() for these */
2696     case SPE_CURE_SICKNESS:
2697     case WAN_MAKE_INVISIBLE:
2698     case WAN_CANCELLATION:
2699     case SPE_CANCELLATION:
2700     case WAN_POLYMORPH:
2701     case SPE_POLYMORPH:
2702     case WAN_STRIKING:
2703     case SPE_FORCE_BOLT:
2704     case WAN_SLOW_MONSTER:
2705     case SPE_SLOW_MONSTER:
2706     case WAN_SPEED_MONSTER:
2707     case SPE_HEALING:
2708     case SPE_EXTRA_HEALING:
2709     case SPE_DRAIN_LIFE:
2710     case WAN_OPENING:
2711     case SPE_KNOCK:
2712         (void) bhitm(u.usteed, obj);
2713         steedhit = TRUE;
2714         break;
2715 
2716     default:
2717         steedhit = FALSE;
2718         break;
2719     }
2720     return steedhit;
2721 }
2722 
2723 /*
2724  * cancel a monster (possibly the hero).  inventory is cancelled only
2725  * if the monster is zapping itself directly, since otherwise the
2726  * effect is too strong.  currently non-hero monsters do not zap
2727  * themselves with cancellation.
2728  */
2729 boolean
cancel_monst(mdef,obj,youattack,allow_cancel_kill,self_cancel)2730 cancel_monst(mdef, obj, youattack, allow_cancel_kill, self_cancel)
2731 register struct monst *mdef;
2732 register struct obj *obj;
2733 boolean youattack, allow_cancel_kill, self_cancel;
2734 {
2735     boolean youdefend = (mdef == &youmonst);
2736     static const char writing_vanishes[] =
2737         "Some writing vanishes from %s head!";
2738     static const char your[] = "your"; /* should be extern */
2739 
2740     if (youdefend ? (!youattack && Antimagic)
2741                   : resist(mdef, obj->oclass, 0, NOTELL))
2742         return FALSE; /* resisted cancellation */
2743 
2744     if (self_cancel) { /* 1st cancel inventory */
2745         struct obj *otmp;
2746 
2747         for (otmp = (youdefend ? invent : mdef->minvent); otmp;
2748              otmp = otmp->nobj)
2749             cancel_item(otmp);
2750         if (youdefend) {
2751             context.botl = 1; /* potential AC change */
2752             find_ac();
2753         }
2754     }
2755 
2756     /* now handle special cases */
2757     if (youdefend) {
2758         if (Upolyd) { /* includes lycanthrope in creature form */
2759             /*
2760              * Return to normal form unless Unchanging.
2761              * Hero in clay golem form dies if Unchanging.
2762              * Does not cure lycanthropy or stop timed random polymorph.
2763              */
2764             if (u.umonnum == PM_CLAY_GOLEM) {
2765                 if (!Blind)
2766                     pline(writing_vanishes, your);
2767                 else /* note: "dark" rather than "heavy" is intentional... */
2768                     You_feel("%s headed.", Hallucination ? "dark" : "light");
2769                 u.mh = 0; /* fatal; death handled by rehumanize() */
2770             }
2771             if (Unchanging && u.mh > 0)
2772                 Your("amulet grows hot for a moment, then cools.");
2773             else
2774                 rehumanize();
2775         }
2776     } else {
2777         mdef->mcan = 1;
2778         /* force shapeshifter into its base form */
2779         if (M_AP_TYPE(mdef) != M_AP_NOTHING)
2780             seemimic(mdef);
2781         /* [not 'else if'; chameleon might have been hiding as a mimic] */
2782         if (mdef->cham >= LOW_PM) {
2783             /* note: newcham() uncancels shapechangers (resets m->mcan
2784                to 0), but only for shapechangers whose m->cham is already
2785                NON_PM and we just verified that it's LOW_PM or higher */
2786             newcham(mdef, &mons[mdef->cham], FALSE, FALSE);
2787             mdef->cham = NON_PM; /* cancelled shapeshifter can't shift */
2788         }
2789         if (is_were(mdef->data) && !is_human(mdef->data))
2790             were_change(mdef);
2791 
2792         if (mdef->data == &mons[PM_CLAY_GOLEM]) {
2793             if (canseemon(mdef))
2794                 pline(writing_vanishes, s_suffix(mon_nam(mdef)));
2795             /* !allow_cancel_kill is for Magicbane, where clay golem
2796                will be killed somewhere back up the call/return chain... */
2797             if (allow_cancel_kill) {
2798                 if (youattack)
2799                     killed(mdef);
2800                 else
2801                     monkilled(mdef, "", AD_SPEL);
2802             }
2803         }
2804     }
2805     return TRUE;
2806 }
2807 
2808 /* you've zapped an immediate type wand up or down */
2809 STATIC_OVL boolean
zap_updown(obj)2810 zap_updown(obj)
2811 struct obj *obj; /* wand or spell */
2812 {
2813     boolean striking = FALSE, disclose = FALSE;
2814     int x, y, xx, yy, ptmp;
2815     struct obj *otmp;
2816     struct engr *e;
2817     struct trap *ttmp;
2818     char buf[BUFSZ];
2819 
2820     /* some wands have special effects other than normal bhitpile */
2821     /* drawbridge might change <u.ux,u.uy> */
2822     x = xx = u.ux;     /* <x,y> is zap location */
2823     y = yy = u.uy;     /* <xx,yy> is drawbridge (portcullis) position */
2824     ttmp = t_at(x, y); /* trap if there is one */
2825 
2826     switch (obj->otyp) {
2827     case WAN_PROBING:
2828         ptmp = 0;
2829         if (u.dz < 0) {
2830             You("probe towards the %s.", ceiling(x, y));
2831         } else {
2832             ptmp += bhitpile(obj, bhito, x, y, u.dz);
2833             You("probe beneath the %s.", surface(x, y));
2834             ptmp += display_binventory(x, y, TRUE);
2835         }
2836         if (!ptmp)
2837             Your("probe reveals nothing.");
2838         return TRUE; /* we've done our own bhitpile */
2839     case WAN_OPENING:
2840     case SPE_KNOCK:
2841         /* up or down, but at closed portcullis only */
2842         if (is_db_wall(x, y) && find_drawbridge(&xx, &yy)) {
2843             open_drawbridge(xx, yy);
2844             disclose = TRUE;
2845         } else if (u.dz > 0 && (x == xdnstair && y == ydnstair)
2846                    /* can't use the stairs down to quest level 2 until
2847                       leader "unlocks" them; give feedback if you try */
2848                    && on_level(&u.uz, &qstart_level) && !ok_to_quest()) {
2849             pline_The("stairs seem to ripple momentarily.");
2850             disclose = TRUE;
2851         }
2852         /* down will release you from bear trap or web */
2853         if (u.dz > 0 && u.utrap) {
2854             (void) openholdingtrap(&youmonst, &disclose);
2855             /* down will trigger trapdoor, hole, or [spiked-] pit */
2856         } else if (u.dz > 0 && !u.utrap) {
2857             (void) openfallingtrap(&youmonst, FALSE, &disclose);
2858         }
2859         break;
2860     case WAN_STRIKING:
2861     case SPE_FORCE_BOLT:
2862         striking = TRUE;
2863         /*FALLTHRU*/
2864     case WAN_LOCKING:
2865     case SPE_WIZARD_LOCK:
2866         /* down at open bridge or up or down at open portcullis */
2867         if (((levl[x][y].typ == DRAWBRIDGE_DOWN)
2868                  ? (u.dz > 0)
2869                  : (is_drawbridge_wall(x, y) >= 0 && !is_db_wall(x, y)))
2870             && find_drawbridge(&xx, &yy)) {
2871             if (!striking)
2872                 close_drawbridge(xx, yy);
2873             else
2874                 destroy_drawbridge(xx, yy);
2875             disclose = TRUE;
2876         } else if (striking && u.dz < 0 && rn2(3) && !Is_airlevel(&u.uz)
2877                    && !Is_waterlevel(&u.uz) && !Underwater
2878                    && !Is_qstart(&u.uz)) {
2879             int dmg;
2880             /* similar to zap_dig() */
2881             pline("A rock is dislodged from the %s and falls on your %s.",
2882                   ceiling(x, y), body_part(HEAD));
2883             dmg = rnd((uarmh && is_metallic(uarmh)) ? 2 : 6);
2884             losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN);
2885             if ((otmp = mksobj_at(ROCK, x, y, FALSE, FALSE)) != 0) {
2886                 (void) xname(otmp); /* set dknown, maybe bknown */
2887                 stackobj(otmp);
2888             }
2889             newsym(x, y);
2890         } else if (u.dz > 0 && ttmp) {
2891             if (!striking && closeholdingtrap(&youmonst, &disclose)) {
2892                 ; /* now stuck in web or bear trap */
2893             } else if (striking && ttmp->ttyp == TRAPDOOR) {
2894                 /* striking transforms trapdoor into hole */
2895                 if (Blind && !ttmp->tseen) {
2896                     pline("%s beneath you shatters.", Something);
2897                 } else if (!ttmp->tseen) { /* => !Blind */
2898                     pline("There's a trapdoor beneath you; it shatters.");
2899                 } else {
2900                     pline("The trapdoor beneath you shatters.");
2901                     disclose = TRUE;
2902                 }
2903                 ttmp->ttyp = HOLE;
2904                 ttmp->tseen = 1;
2905                 newsym(x, y);
2906                 /* might fall down hole */
2907                 dotrap(ttmp, 0);
2908             } else if (!striking && ttmp->ttyp == HOLE) {
2909                 /* locking transforms hole into trapdoor */
2910                 ttmp->ttyp = TRAPDOOR;
2911                 if (Blind || !ttmp->tseen) {
2912                     pline("Some %s swirls beneath you.",
2913                           is_ice(x, y) ? "frost" : "dust");
2914                 } else {
2915                     ttmp->tseen = 1;
2916                     newsym(x, y);
2917                     pline("A trapdoor appears beneath you.");
2918                     disclose = TRUE;
2919                 }
2920                 /* hadn't fallen down hole; won't fall now */
2921             }
2922         }
2923         break;
2924     case SPE_STONE_TO_FLESH:
2925         if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) || Underwater
2926             || (Is_qstart(&u.uz) && u.dz < 0)) {
2927             pline1(nothing_happens);
2928         } else if (u.dz < 0) { /* we should do more... */
2929             pline("Blood drips on your %s.", body_part(FACE));
2930         } else if (u.dz > 0 && !OBJ_AT(u.ux, u.uy)) {
2931             /*
2932             Print this message only if there wasn't an engraving
2933             affected here.  If water or ice, act like waterlevel case.
2934             */
2935             e = engr_at(u.ux, u.uy);
2936             if (!(e && e->engr_type == ENGRAVE)) {
2937                 if (is_pool(u.ux, u.uy) || is_ice(u.ux, u.uy))
2938                     pline1(nothing_happens);
2939                 else
2940                     pline("Blood %ss %s your %s.",
2941                           is_lava(u.ux, u.uy) ? "boil" : "pool",
2942                           Levitation ? "beneath" : "at",
2943                           makeplural(body_part(FOOT)));
2944             }
2945         }
2946         break;
2947     default:
2948         break;
2949     }
2950 
2951     if (u.dz > 0) {
2952         /* zapping downward */
2953         (void) bhitpile(obj, bhito, x, y, u.dz);
2954 
2955         /* subset of engraving effects; none sets `disclose' */
2956         if ((e = engr_at(x, y)) != 0 && e->engr_type != HEADSTONE) {
2957             switch (obj->otyp) {
2958             case WAN_POLYMORPH:
2959             case SPE_POLYMORPH:
2960                 del_engr(e);
2961                 make_engr_at(x, y, random_engraving(buf), moves, (xchar) 0);
2962                 break;
2963             case WAN_CANCELLATION:
2964             case SPE_CANCELLATION:
2965             case WAN_MAKE_INVISIBLE:
2966                 del_engr(e);
2967                 break;
2968             case WAN_TELEPORTATION:
2969             case SPE_TELEPORT_AWAY:
2970                 rloc_engr(e);
2971                 break;
2972             case SPE_STONE_TO_FLESH:
2973                 if (e->engr_type == ENGRAVE) {
2974                     /* only affects things in stone */
2975                     pline_The(Hallucination
2976                                   ? "floor runs like butter!"
2977                                   : "edges on the floor get smoother.");
2978                     wipe_engr_at(x, y, d(2, 4), TRUE);
2979                 }
2980                 break;
2981             case WAN_STRIKING:
2982             case SPE_FORCE_BOLT:
2983                 wipe_engr_at(x, y, d(2, 4), TRUE);
2984                 break;
2985             default:
2986                 break;
2987             }
2988         }
2989     } else if (u.dz < 0) {
2990         /* zapping upward */
2991 
2992         /* game flavor: if you're hiding under "something"
2993          * a zap upward should hit that "something".
2994          */
2995         if (u.uundetected && hides_under(youmonst.data)) {
2996             int hitit = 0;
2997             otmp = level.objects[u.ux][u.uy];
2998 
2999             if (otmp)
3000                 hitit = bhito(otmp, obj);
3001             if (hitit) {
3002                 (void) hideunder(&youmonst);
3003                 disclose = TRUE;
3004             }
3005         }
3006     }
3007 
3008     return disclose;
3009 }
3010 
3011 /* used by do_break_wand() was well as by weffects() */
3012 void
zapsetup()3013 zapsetup()
3014 {
3015     obj_zapped = FALSE;
3016 }
3017 
3018 void
zapwrapup()3019 zapwrapup()
3020 {
3021     /* if do_osshock() set obj_zapped while polying, give a message now */
3022     if (obj_zapped)
3023         You_feel("shuddering vibrations.");
3024     obj_zapped = FALSE;
3025 }
3026 
3027 /* called for various wand and spell effects - M. Stephenson */
3028 void
weffects(obj)3029 weffects(obj)
3030 struct obj *obj;
3031 {
3032     int otyp = obj->otyp;
3033     boolean disclose = FALSE, was_unkn = !objects[otyp].oc_name_known;
3034 
3035     exercise(A_WIS, TRUE);
3036     if (u.usteed && (objects[otyp].oc_dir != NODIR) && !u.dx && !u.dy
3037         && (u.dz > 0) && zap_steed(obj)) {
3038         disclose = TRUE;
3039     } else if (objects[otyp].oc_dir == IMMEDIATE) {
3040         zapsetup(); /* reset obj_zapped */
3041         if (u.uswallow) {
3042             (void) bhitm(u.ustuck, obj);
3043             /* [how about `bhitpile(u.ustuck->minvent)' effect?] */
3044         } else if (u.dz) {
3045             disclose = zap_updown(obj);
3046         } else {
3047             (void) bhit(u.dx, u.dy, rn1(8, 6), ZAPPED_WAND, bhitm, bhito,
3048                         &obj);
3049         }
3050         zapwrapup(); /* give feedback for obj_zapped */
3051 
3052     } else if (objects[otyp].oc_dir == NODIR) {
3053         zapnodir(obj);
3054 
3055     } else {
3056         /* neither immediate nor directionless */
3057 
3058         if (otyp == WAN_DIGGING || otyp == SPE_DIG)
3059             zap_dig();
3060         else if (otyp >= SPE_MAGIC_MISSILE && otyp <= SPE_FINGER_OF_DEATH)
3061             buzz(otyp - SPE_MAGIC_MISSILE + 10, u.ulevel / 2 + 1, u.ux, u.uy,
3062                  u.dx, u.dy);
3063         else if (otyp >= WAN_MAGIC_MISSILE && otyp <= WAN_LIGHTNING)
3064             buzz(otyp - WAN_MAGIC_MISSILE,
3065                  (otyp == WAN_MAGIC_MISSILE) ? 2 : 6, u.ux, u.uy, u.dx, u.dy);
3066         else
3067             impossible("weffects: unexpected spell or wand");
3068         disclose = TRUE;
3069     }
3070     if (disclose) {
3071         learnwand(obj);
3072         if (was_unkn)
3073             more_experienced(0, 10);
3074     }
3075     return;
3076 }
3077 
3078 /* augment damage for a spell dased on the hero's intelligence (and level) */
3079 int
spell_damage_bonus(dmg)3080 spell_damage_bonus(dmg)
3081 int dmg; /* base amount to be adjusted by bonus or penalty */
3082 {
3083     int intell = ACURR(A_INT);
3084 
3085     /* Punish low intelligence before low level else low intelligence
3086        gets punished only when high level */
3087     if (intell <= 9) {
3088         /* -3 penalty, but never reduce combined amount below 1
3089            (if dmg is 0 for some reason, we're careful to leave it there) */
3090         if (dmg > 1)
3091             dmg = (dmg <= 3) ? 1 : dmg - 3;
3092     } else if (intell <= 13 || u.ulevel < 5)
3093         ; /* no bonus or penalty; dmg remains same */
3094     else if (intell <= 18)
3095         dmg += 1;
3096     else if (intell <= 24 || u.ulevel < 14)
3097         dmg += 2;
3098     else
3099         dmg += 3; /* Int 25 */
3100 
3101     return dmg;
3102 }
3103 
3104 /*
3105  * Generate the to hit bonus for a spell.  Based on the hero's skill in
3106  * spell class and dexterity.
3107  */
3108 STATIC_OVL int
spell_hit_bonus(skill)3109 spell_hit_bonus(skill)
3110 int skill;
3111 {
3112     int hit_bon = 0;
3113     int dex = ACURR(A_DEX);
3114 
3115     switch (P_SKILL(spell_skilltype(skill))) {
3116     case P_ISRESTRICTED:
3117     case P_UNSKILLED:
3118         hit_bon = -4;
3119         break;
3120     case P_BASIC:
3121         hit_bon = 0;
3122         break;
3123     case P_SKILLED:
3124         hit_bon = 2;
3125         break;
3126     case P_EXPERT:
3127         hit_bon = 3;
3128         break;
3129     }
3130 
3131     if (dex < 4)
3132         hit_bon -= 3;
3133     else if (dex < 6)
3134         hit_bon -= 2;
3135     else if (dex < 8)
3136         hit_bon -= 1;
3137     else if (dex < 14)
3138         /* Will change when print stuff below removed */
3139         hit_bon -= 0;
3140     else
3141         /* Even increment for dextrous heroes (see weapon.c abon) */
3142         hit_bon += dex - 14;
3143 
3144     return hit_bon;
3145 }
3146 
3147 const char *
exclam(force)3148 exclam(force)
3149 int force;
3150 {
3151     /* force == 0 occurs e.g. with sleep ray */
3152     /* note that large force is usual with wands so that !! would
3153             require information about hand/weapon/wand */
3154     return (const char *) ((force < 0) ? "?" : (force <= 4) ? "." : "!");
3155 }
3156 
3157 void
hit(str,mtmp,force)3158 hit(str, mtmp, force)
3159 const char *str;
3160 struct monst *mtmp;
3161 const char *force; /* usually either "." or "!" */
3162 {
3163     if ((!cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp)
3164          && !(u.uswallow && mtmp == u.ustuck)) || !flags.verbose)
3165         pline("%s %s it.", The(str), vtense(str, "hit"));
3166     else
3167         pline("%s %s %s%s", The(str), vtense(str, "hit"),
3168               mon_nam(mtmp), force);
3169 }
3170 
3171 void
miss(str,mtmp)3172 miss(str, mtmp)
3173 register const char *str;
3174 register struct monst *mtmp;
3175 {
3176     pline(
3177         "%s %s %s.", The(str), vtense(str, "miss"),
3178         ((cansee(bhitpos.x, bhitpos.y) || canspotmon(mtmp)) && flags.verbose)
3179             ? mon_nam(mtmp)
3180             : "it");
3181 }
3182 
3183 STATIC_OVL void
skiprange(range,skipstart,skipend)3184 skiprange(range, skipstart, skipend)
3185 int range, *skipstart, *skipend;
3186 {
3187     int tr = (range / 4);
3188     int tmp = range - ((tr > 0) ? rnd(tr) : 0);
3189 
3190     *skipstart = tmp;
3191     *skipend = tmp - ((tmp / 4) * rnd(3));
3192     if (*skipend >= tmp)
3193         *skipend = tmp - 1;
3194 }
3195 
3196 /*
3197  *  Called for the following distance effects:
3198  *      when a weapon is thrown (weapon == THROWN_WEAPON)
3199  *      when an object is kicked (KICKED_WEAPON)
3200  *      when an IMMEDIATE wand is zapped (ZAPPED_WAND)
3201  *      when a light beam is flashed (FLASHED_LIGHT)
3202  *      when a mirror is applied (INVIS_BEAM)
3203  *  A thrown/kicked object falls down at end of its range or when a monster
3204  *  is hit.  The variable 'bhitpos' is set to the final position of the weapon
3205  *  thrown/zapped.  The ray of a wand may affect (by calling a provided
3206  *  function) several objects and monsters on its path.  The return value
3207  *  is the monster hit (weapon != ZAPPED_WAND), or a null monster pointer.
3208  *
3209  * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be
3210  * destroyed and *pobj set to NULL to indicate this.
3211  *
3212  *  Check !u.uswallow before calling bhit().
3213  *  This function reveals the absence of a remembered invisible monster in
3214  *  necessary cases (throwing or kicking weapons).  The presence of a real
3215  *  one is revealed for a weapon, but if not a weapon is left up to fhitm().
3216  */
3217 struct monst *
bhit(ddx,ddy,range,weapon,fhitm,fhito,pobj)3218 bhit(ddx, ddy, range, weapon, fhitm, fhito, pobj)
3219 register int ddx, ddy, range;          /* direction and range */
3220 enum bhit_call_types weapon;           /* defined in hack.h */
3221 int FDECL((*fhitm), (MONST_P, OBJ_P)), /* fns called when mon/obj hit */
3222     FDECL((*fhito), (OBJ_P, OBJ_P));
3223 struct obj **pobj; /* object tossed/used, set to NULL
3224                     * if object is destroyed */
3225 {
3226     struct monst *mtmp, *result = (struct monst *) 0;
3227     struct obj *obj = *pobj;
3228     uchar typ;
3229     boolean shopdoor = FALSE, point_blank = TRUE;
3230     boolean in_skip = FALSE, allow_skip = FALSE;
3231     boolean tethered_weapon = FALSE;
3232     int skiprange_start = 0, skiprange_end = 0, skipcount = 0;
3233 
3234     if (weapon == KICKED_WEAPON) {
3235         /* object starts one square in front of player */
3236         bhitpos.x = u.ux + ddx;
3237         bhitpos.y = u.uy + ddy;
3238         range--;
3239     } else {
3240         bhitpos.x = u.ux;
3241         bhitpos.y = u.uy;
3242     }
3243 
3244     if (weapon == THROWN_WEAPON && obj && obj->otyp == ROCK) {
3245         skiprange(range, &skiprange_start, &skiprange_end);
3246         allow_skip = !rn2(3);
3247     }
3248 
3249     if (weapon == FLASHED_LIGHT) {
3250         tmp_at(DISP_BEAM, cmap_to_glyph(S_flashbeam));
3251     } else if (weapon == THROWN_TETHERED_WEAPON && obj) {
3252         tethered_weapon = TRUE;
3253         weapon = THROWN_WEAPON; /* simplify 'if's that follow below */
3254         tmp_at(DISP_TETHER, obj_to_glyph(obj, rn2_on_display_rng));
3255     } else if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
3256         tmp_at(DISP_FLASH, obj_to_glyph(obj, rn2_on_display_rng));
3257 
3258     while (range-- > 0) {
3259         int x, y;
3260 
3261         bhitpos.x += ddx;
3262         bhitpos.y += ddy;
3263         x = bhitpos.x;
3264         y = bhitpos.y;
3265 
3266         if (!isok(x, y)) {
3267             bhitpos.x -= ddx;
3268             bhitpos.y -= ddy;
3269             break;
3270         }
3271 
3272         if (is_pick(obj) && inside_shop(x, y)
3273             && (mtmp = shkcatch(obj, x, y)) != 0) {
3274             tmp_at(DISP_END, 0);
3275             result = mtmp;
3276             goto bhit_done;
3277         }
3278 
3279         typ = levl[bhitpos.x][bhitpos.y].typ;
3280 
3281         /* iron bars will block anything big enough and break some things */
3282         if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) {
3283             if (typ == IRONBARS
3284                 && hits_bars(pobj, x - ddx, y - ddy, bhitpos.x, bhitpos.y,
3285                              point_blank ? 0 : !rn2(5), 1)) {
3286                 /* caveat: obj might now be null... */
3287                 obj = *pobj;
3288                 bhitpos.x -= ddx;
3289                 bhitpos.y -= ddy;
3290                 break;
3291             } else if (obj->lamplit && !Blind) {
3292                 show_transient_light(obj, bhitpos.x, bhitpos.y);
3293             }
3294         }
3295 
3296         if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) {
3297             boolean learn_it = FALSE;
3298 
3299             switch (obj->otyp) {
3300             case WAN_OPENING:
3301             case SPE_KNOCK:
3302                 if (is_db_wall(bhitpos.x, bhitpos.y)) {
3303                     if (cansee(x, y) || cansee(bhitpos.x, bhitpos.y))
3304                         learn_it = TRUE;
3305                     open_drawbridge(x, y);
3306                 }
3307                 break;
3308             case WAN_LOCKING:
3309             case SPE_WIZARD_LOCK:
3310                 if ((cansee(x, y) || cansee(bhitpos.x, bhitpos.y))
3311                     && levl[x][y].typ == DRAWBRIDGE_DOWN)
3312                     learn_it = TRUE;
3313                 close_drawbridge(x, y);
3314                 break;
3315             case WAN_STRIKING:
3316             case SPE_FORCE_BOLT:
3317                 if (typ != DRAWBRIDGE_UP)
3318                     destroy_drawbridge(x, y);
3319                 learn_it = TRUE;
3320                 break;
3321             }
3322             if (learn_it)
3323                 learnwand(obj);
3324         }
3325 
3326         mtmp = m_at(bhitpos.x, bhitpos.y);
3327 
3328         /*
3329          * skipping rocks
3330          *
3331          * skiprange_start is only set if this is a thrown rock
3332          */
3333         if (skiprange_start && (range == skiprange_start) && allow_skip) {
3334             if (is_pool(bhitpos.x, bhitpos.y) && !mtmp) {
3335                 in_skip = TRUE;
3336                 if (!Blind)
3337                     pline("%s %s%s.", Yname2(obj), otense(obj, "skip"),
3338                           skipcount ? " again" : "");
3339                 else
3340                     You_hear("%s skip.", yname(obj));
3341                 skipcount++;
3342             } else if (skiprange_start > skiprange_end + 1) {
3343                 --skiprange_start;
3344             }
3345         }
3346         if (in_skip) {
3347             if (range <= skiprange_end) {
3348                 in_skip = FALSE;
3349                 if (range > 3) /* another bounce? */
3350                     skiprange(range, &skiprange_start, &skiprange_end);
3351             } else if (mtmp && M_IN_WATER(mtmp->data)) {
3352                 if (!Blind && canspotmon(mtmp))
3353                     pline("%s %s over %s.", Yname2(obj), otense(obj, "pass"),
3354                           mon_nam(mtmp));
3355                 mtmp = (struct monst *) 0;
3356             }
3357         }
3358 
3359         /* if mtmp is a shade and missile passes harmlessly through it,
3360            give message and skip it in order to keep going */
3361         if (mtmp && (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
3362             && shade_miss(&youmonst, mtmp, obj, TRUE, TRUE))
3363             mtmp = (struct monst *) 0;
3364 
3365         if (mtmp) {
3366             notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
3367             if (weapon == FLASHED_LIGHT) {
3368                 /* FLASHED_LIGHT hitting invisible monster should
3369                    pass through instead of stop so we call
3370                    flash_hits_mon() directly rather than returning
3371                    mtmp back to caller.  That allows the flash to
3372                    keep on going.  Note that we use mtmp->minvis
3373                    not canspotmon() because it makes no difference
3374                    whether the hero can see the monster or not. */
3375                 if (mtmp->minvis) {
3376                     obj->ox = u.ux, obj->oy = u.uy;
3377                     (void) flash_hits_mon(mtmp, obj);
3378                 } else {
3379                     tmp_at(DISP_END, 0);
3380                     result = mtmp; /* caller will call flash_hits_mon */
3381                     goto bhit_done;
3382                 }
3383             } else if (weapon == INVIS_BEAM) {
3384                 /* Like FLASHED_LIGHT, INVIS_BEAM should continue
3385                    through invisible targets; unlike it, we aren't
3386                    prepared for multiple hits so just get first one
3387                    that's either visible or could see its invisible
3388                    self.  [No tmp_at() cleanup is needed here.] */
3389                 if (!mtmp->minvis || perceives(mtmp->data)) {
3390                     result = mtmp;
3391                     goto bhit_done;
3392                 }
3393             } else if (weapon != ZAPPED_WAND) {
3394 
3395                 /* THROWN_WEAPON, KICKED_WEAPON */
3396                 if (!tethered_weapon)
3397                     tmp_at(DISP_END, 0);
3398 
3399                 if (cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp))
3400                     map_invisible(bhitpos.x, bhitpos.y);
3401                 result = mtmp;
3402                 goto bhit_done;
3403             } else {
3404                 /* ZAPPED_WAND */
3405                 (*fhitm)(mtmp, obj);
3406                 range -= 3;
3407             }
3408         } else {
3409             if (weapon == ZAPPED_WAND && obj->otyp == WAN_PROBING
3410                 && glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)) {
3411                 unmap_object(bhitpos.x, bhitpos.y);
3412                 newsym(x, y);
3413             }
3414         }
3415         if (fhito) {
3416             if (bhitpile(obj, fhito, bhitpos.x, bhitpos.y, 0))
3417                 range--;
3418         } else {
3419             if (weapon == KICKED_WEAPON
3420                 && ((obj->oclass == COIN_CLASS
3421                      && OBJ_AT(bhitpos.x, bhitpos.y))
3422                     || ship_object(obj, bhitpos.x, bhitpos.y,
3423                                    costly_spot(bhitpos.x, bhitpos.y)))) {
3424                 tmp_at(DISP_END, 0);
3425                 goto bhit_done; /* result == (struct monst *) 0 */
3426             }
3427         }
3428         if (weapon == ZAPPED_WAND && (IS_DOOR(typ) || typ == SDOOR)) {
3429             switch (obj->otyp) {
3430             case WAN_OPENING:
3431             case WAN_LOCKING:
3432             case WAN_STRIKING:
3433             case SPE_KNOCK:
3434             case SPE_WIZARD_LOCK:
3435             case SPE_FORCE_BOLT:
3436                 if (doorlock(obj, bhitpos.x, bhitpos.y)) {
3437                     if (cansee(bhitpos.x, bhitpos.y)
3438                         || (obj->otyp == WAN_STRIKING && !Deaf))
3439                         learnwand(obj);
3440                     if (levl[bhitpos.x][bhitpos.y].doormask == D_BROKEN
3441                         && *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE)) {
3442                         shopdoor = TRUE;
3443                         add_damage(bhitpos.x, bhitpos.y, SHOP_DOOR_COST);
3444                     }
3445                 }
3446                 break;
3447             }
3448         }
3449         if (!ZAP_POS(typ) || closed_door(bhitpos.x, bhitpos.y)) {
3450             bhitpos.x -= ddx;
3451             bhitpos.y -= ddy;
3452             break;
3453         }
3454         if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) {
3455             /* 'I' present but no monster: erase */
3456             /* do this before the tmp_at() */
3457             if (glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)
3458                 && cansee(x, y)) {
3459                 unmap_object(bhitpos.x, bhitpos.y);
3460                 newsym(x, y);
3461             }
3462             tmp_at(bhitpos.x, bhitpos.y);
3463             delay_output();
3464             /* kicked objects fall in pools */
3465             if ((weapon == KICKED_WEAPON)
3466                 && (is_pool(bhitpos.x, bhitpos.y)
3467                     || is_lava(bhitpos.x, bhitpos.y)))
3468                 break;
3469             if (IS_SINK(typ) && weapon != FLASHED_LIGHT)
3470                 break; /* physical objects fall onto sink */
3471         }
3472         /* limit range of ball so hero won't make an invalid move */
3473         if (weapon == THROWN_WEAPON && range > 0
3474             && obj->otyp == HEAVY_IRON_BALL) {
3475             struct obj *bobj;
3476             struct trap *t;
3477 
3478             if ((bobj = sobj_at(BOULDER, x, y)) != 0) {
3479                 if (cansee(x, y))
3480                     pline("%s hits %s.", The(distant_name(obj, xname)),
3481                           an(xname(bobj)));
3482                 range = 0;
3483             } else if (obj == uball) {
3484                 if (!test_move(x - ddx, y - ddy, ddx, ddy, TEST_MOVE)) {
3485                     /* nb: it didn't hit anything directly */
3486                     if (cansee(x, y))
3487                         pline("%s jerks to an abrupt halt.",
3488                               The(distant_name(obj, xname))); /* lame */
3489                     range = 0;
3490                 } else if (Sokoban && (t = t_at(x, y)) != 0
3491                            && (is_pit(t->ttyp) || is_hole(t->ttyp))) {
3492                     /* hero falls into the trap, so ball stops */
3493                     range = 0;
3494                 }
3495             }
3496         }
3497 
3498         /* thrown/kicked missile has moved away from its starting spot */
3499         point_blank = FALSE; /* affects passing through iron bars */
3500     }
3501 
3502     if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM && !tethered_weapon)
3503         tmp_at(DISP_END, 0);
3504 
3505     if (shopdoor)
3506         pay_for_damage("destroy", FALSE);
3507 
3508  bhit_done:
3509     if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
3510         transient_light_cleanup();
3511 
3512     return result;
3513 }
3514 
3515 /* process thrown boomerang, which travels a curving path...
3516  * A multi-shot volley ought to have all missiles in flight at once,
3517  * but we're called separately for each one.  We terminate the volley
3518  * early on a failed catch since continuing to throw after being hit
3519  * is too obviously silly.
3520  */
3521 struct monst *
boomhit(obj,dx,dy)3522 boomhit(obj, dx, dy)
3523 struct obj *obj;
3524 int dx, dy;
3525 {
3526     register int i, ct;
3527     int boom; /* showsym[] index  */
3528     struct monst *mtmp;
3529     boolean counterclockwise = TRUE; /* right-handed throw */
3530 
3531     /* counterclockwise traversal patterns:
3532      *  ..........................54.................................
3533      *  ..................43.....6..3....765.........................
3534      *  ..........32.....5..2...7...2...8...4....87..................
3535      *  .........4..1....6..1...8..1....9...3...9..6.....98..........
3536      *  ..21@....5...@...7..@....9@......@12....@...5...@..7.....@9..
3537      *  .3...9....6..9....89.....................1..4...1..6....1..8.
3538      *  .4...8.....78.............................23....2..5...2...7.
3539      *  ..567............................................34....3..6..
3540      *  ........................................................45...
3541      * (invert rows for corresponding clockwise patterns)
3542      */
3543 
3544     bhitpos.x = u.ux;
3545     bhitpos.y = u.uy;
3546     boom = counterclockwise ? S_boomleft : S_boomright;
3547     for (i = 0; i < 8; i++)
3548         if (xdir[i] == dx && ydir[i] == dy)
3549             break;
3550     tmp_at(DISP_FLASH, cmap_to_glyph(boom));
3551     for (ct = 0; ct < 10; ct++) {
3552         i = (i + 8) % 8;                          /* 0..7 (8 -> 0, -1 -> 7) */
3553         boom = (S_boomleft + S_boomright - boom); /* toggle */
3554         tmp_at(DISP_CHANGE, cmap_to_glyph(boom)); /* change glyph */
3555         dx = xdir[i];
3556         dy = ydir[i];
3557         bhitpos.x += dx;
3558         bhitpos.y += dy;
3559         if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
3560             m_respond(mtmp);
3561             tmp_at(DISP_END, 0);
3562             return mtmp;
3563         }
3564         if (!ZAP_POS(levl[bhitpos.x][bhitpos.y].typ)
3565             || closed_door(bhitpos.x, bhitpos.y)) {
3566             bhitpos.x -= dx;
3567             bhitpos.y -= dy;
3568             break;
3569         }
3570         if (bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */
3571             if (Fumbling || rn2(20) >= ACURR(A_DEX)) {
3572                 /* we hit ourselves */
3573                 (void) thitu(10 + obj->spe, dmgval(obj, &youmonst), &obj,
3574                              "boomerang");
3575                 endmultishot(TRUE);
3576                 break;
3577             } else { /* we catch it */
3578                 tmp_at(DISP_END, 0);
3579                 You("skillfully catch the boomerang.");
3580                 return &youmonst;
3581             }
3582         }
3583         tmp_at(bhitpos.x, bhitpos.y);
3584         delay_output();
3585         if (IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) {
3586             if (!Deaf)
3587                 pline("Klonk!");
3588             break; /* boomerang falls on sink */
3589         }
3590         /* ct==0, initial position, we want next delta to be same;
3591            ct==5, opposite position, repeat delta undoes first one */
3592         if (ct % 5 != 0)
3593             i += (counterclockwise ? -1 : 1);
3594     }
3595     tmp_at(DISP_END, 0); /* do not leave last symbol */
3596     return (struct monst *) 0;
3597 }
3598 
3599 /* used by buzz(); also used by munslime(muse.c); returns damage applied
3600    to mon; note: caller is responsible for killing mon if damage is fatal */
3601 int
zhitm(mon,type,nd,ootmp)3602 zhitm(mon, type, nd, ootmp)
3603 register struct monst *mon;
3604 register int type, nd;
3605 struct obj **ootmp; /* to return worn armor for caller to disintegrate */
3606 {
3607     register int tmp = 0;
3608     register int abstype = abs(type) % 10;
3609     boolean sho_shieldeff = FALSE;
3610     boolean spellcaster = is_hero_spell(type); /* maybe get a bonus! */
3611 
3612     *ootmp = (struct obj *) 0;
3613     switch (abstype) {
3614     case ZT_MAGIC_MISSILE:
3615         if (resists_magm(mon)) {
3616             sho_shieldeff = TRUE;
3617             break;
3618         }
3619         tmp = d(nd, 6);
3620         if (spellcaster)
3621             tmp = spell_damage_bonus(tmp);
3622         break;
3623     case ZT_FIRE:
3624         if (resists_fire(mon)) {
3625             sho_shieldeff = TRUE;
3626             break;
3627         }
3628         tmp = d(nd, 6);
3629         if (resists_cold(mon))
3630             tmp += 7;
3631         if (spellcaster)
3632             tmp = spell_damage_bonus(tmp);
3633         if (burnarmor(mon)) {
3634             if (!rn2(3))
3635                 (void) destroy_mitem(mon, POTION_CLASS, AD_FIRE);
3636             if (!rn2(3))
3637                 (void) destroy_mitem(mon, SCROLL_CLASS, AD_FIRE);
3638             if (!rn2(5))
3639                 (void) destroy_mitem(mon, SPBOOK_CLASS, AD_FIRE);
3640             destroy_mitem(mon, FOOD_CLASS, AD_FIRE); /* carried slime */
3641         }
3642         break;
3643     case ZT_COLD:
3644         if (resists_cold(mon)) {
3645             sho_shieldeff = TRUE;
3646             break;
3647         }
3648         tmp = d(nd, 6);
3649         if (resists_fire(mon))
3650             tmp += d(nd, 3);
3651         if (spellcaster)
3652             tmp = spell_damage_bonus(tmp);
3653         if (!rn2(3))
3654             (void) destroy_mitem(mon, POTION_CLASS, AD_COLD);
3655         break;
3656     case ZT_SLEEP:
3657         tmp = 0;
3658         (void) sleep_monst(mon, d(nd, 25),
3659                            type == ZT_WAND(ZT_SLEEP) ? WAND_CLASS : '\0');
3660         break;
3661     case ZT_DEATH:                              /* death/disintegration */
3662         if (abs(type) != ZT_BREATH(ZT_DEATH)) { /* death */
3663             if (mon->data == &mons[PM_DEATH]) {
3664                 mon->mhpmax += mon->mhpmax / 2;
3665                 if (mon->mhpmax >= MAGIC_COOKIE)
3666                     mon->mhpmax = MAGIC_COOKIE - 1;
3667                 mon->mhp = mon->mhpmax;
3668                 tmp = 0;
3669                 break;
3670             }
3671             if (nonliving(mon->data) || is_demon(mon->data)
3672                 || is_vampshifter(mon) || resists_magm(mon)) {
3673                 /* similar to player */
3674                 sho_shieldeff = TRUE;
3675                 break;
3676             }
3677             type = -1; /* so they don't get saving throws */
3678         } else {
3679             struct obj *otmp2;
3680 
3681             if (resists_disint(mon)) {
3682                 sho_shieldeff = TRUE;
3683             } else if (mon->misc_worn_check & W_ARMS) {
3684                 /* destroy shield; victim survives */
3685                 *ootmp = which_armor(mon, W_ARMS);
3686             } else if (mon->misc_worn_check & W_ARM) {
3687                 /* destroy body armor, also cloak if present */
3688                 *ootmp = which_armor(mon, W_ARM);
3689                 if ((otmp2 = which_armor(mon, W_ARMC)) != 0)
3690                     m_useup(mon, otmp2);
3691             } else {
3692                 /* no body armor, victim dies; destroy cloak
3693                    and shirt now in case target gets life-saved */
3694                 tmp = MAGIC_COOKIE;
3695                 if ((otmp2 = which_armor(mon, W_ARMC)) != 0)
3696                     m_useup(mon, otmp2);
3697                 if ((otmp2 = which_armor(mon, W_ARMU)) != 0)
3698                     m_useup(mon, otmp2);
3699             }
3700             type = -1; /* no saving throw wanted */
3701             break;     /* not ordinary damage */
3702         }
3703         tmp = mon->mhp + 1;
3704         break;
3705     case ZT_LIGHTNING:
3706         if (resists_elec(mon)) {
3707             sho_shieldeff = TRUE;
3708             tmp = 0;
3709             /* can still blind the monster */
3710         } else
3711             tmp = d(nd, 6);
3712         if (spellcaster)
3713             tmp = spell_damage_bonus(tmp);
3714         if (!resists_blnd(mon)
3715             && !(type > 0 && u.uswallow && mon == u.ustuck)) {
3716             register unsigned rnd_tmp = rnd(50);
3717             mon->mcansee = 0;
3718             if ((mon->mblinded + rnd_tmp) > 127)
3719                 mon->mblinded = 127;
3720             else
3721                 mon->mblinded += rnd_tmp;
3722         }
3723         if (!rn2(3))
3724             (void) destroy_mitem(mon, WAND_CLASS, AD_ELEC);
3725         /* not actually possible yet */
3726         if (!rn2(3))
3727             (void) destroy_mitem(mon, RING_CLASS, AD_ELEC);
3728         break;
3729     case ZT_POISON_GAS:
3730         if (resists_poison(mon)) {
3731             sho_shieldeff = TRUE;
3732             break;
3733         }
3734         tmp = d(nd, 6);
3735         break;
3736     case ZT_ACID:
3737         if (resists_acid(mon)) {
3738             sho_shieldeff = TRUE;
3739             break;
3740         }
3741         tmp = d(nd, 6);
3742         if (!rn2(6))
3743             acid_damage(MON_WEP(mon));
3744         if (!rn2(6))
3745             erode_armor(mon, ERODE_CORRODE);
3746         break;
3747     }
3748     if (sho_shieldeff)
3749         shieldeff(mon->mx, mon->my);
3750     if (is_hero_spell(type) && (Role_if(PM_KNIGHT) && u.uhave.questart))
3751         tmp *= 2;
3752     if (tmp > 0 && type >= 0
3753         && resist(mon, type < ZT_SPELL(0) ? WAND_CLASS : '\0', 0, NOTELL))
3754         tmp /= 2;
3755     if (tmp < 0)
3756         tmp = 0; /* don't allow negative damage */
3757     debugpline3("zapped monster hp = %d (= %d - %d)", mon->mhp - tmp,
3758                 mon->mhp, tmp);
3759     mon->mhp -= tmp;
3760     return tmp;
3761 }
3762 
3763 STATIC_OVL void
zhitu(type,nd,fltxt,sx,sy)3764 zhitu(type, nd, fltxt, sx, sy)
3765 int type, nd;
3766 const char *fltxt;
3767 xchar sx, sy;
3768 {
3769     int dam = 0, abstyp = abs(type);
3770 
3771     switch (abstyp % 10) {
3772     case ZT_MAGIC_MISSILE:
3773         if (Antimagic) {
3774             shieldeff(sx, sy);
3775             pline_The("missiles bounce off!");
3776         } else {
3777             dam = d(nd, 6);
3778             exercise(A_STR, FALSE);
3779         }
3780         break;
3781     case ZT_FIRE:
3782         if (Fire_resistance) {
3783             shieldeff(sx, sy);
3784             You("don't feel hot!");
3785             ugolemeffects(AD_FIRE, d(nd, 6));
3786         } else {
3787             dam = d(nd, 6);
3788         }
3789         burn_away_slime();
3790         if (burnarmor(&youmonst)) { /* "body hit" */
3791             if (!rn2(3))
3792                 destroy_item(POTION_CLASS, AD_FIRE);
3793             if (!rn2(3))
3794                 destroy_item(SCROLL_CLASS, AD_FIRE);
3795             if (!rn2(5))
3796                 destroy_item(SPBOOK_CLASS, AD_FIRE);
3797             destroy_item(FOOD_CLASS, AD_FIRE);
3798         }
3799         break;
3800     case ZT_COLD:
3801         if (Cold_resistance) {
3802             shieldeff(sx, sy);
3803             You("don't feel cold.");
3804             ugolemeffects(AD_COLD, d(nd, 6));
3805         } else {
3806             dam = d(nd, 6);
3807         }
3808         if (!rn2(3))
3809             destroy_item(POTION_CLASS, AD_COLD);
3810         break;
3811     case ZT_SLEEP:
3812         if (Sleep_resistance) {
3813             shieldeff(u.ux, u.uy);
3814             You("don't feel sleepy.");
3815         } else {
3816             fall_asleep(-d(nd, 25), TRUE); /* sleep ray */
3817         }
3818         break;
3819     case ZT_DEATH:
3820         if (abstyp == ZT_BREATH(ZT_DEATH)) {
3821             if (Disint_resistance) {
3822                 You("are not disintegrated.");
3823                 break;
3824             } else if (uarms) {
3825                 /* destroy shield; other possessions are safe */
3826                 (void) destroy_arm(uarms);
3827                 break;
3828             } else if (uarm) {
3829                 /* destroy suit; if present, cloak goes too */
3830                 if (uarmc)
3831                     (void) destroy_arm(uarmc);
3832                 (void) destroy_arm(uarm);
3833                 break;
3834             }
3835             /* no shield or suit, you're dead; wipe out cloak
3836                and/or shirt in case of life-saving or bones */
3837             if (uarmc)
3838                 (void) destroy_arm(uarmc);
3839             if (uarmu)
3840                 (void) destroy_arm(uarmu);
3841         } else if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
3842             shieldeff(sx, sy);
3843             You("seem unaffected.");
3844             break;
3845         } else if (Antimagic) {
3846             shieldeff(sx, sy);
3847             You("aren't affected.");
3848             break;
3849         }
3850         killer.format = KILLED_BY_AN;
3851         Strcpy(killer.name, fltxt ? fltxt : "");
3852         /* when killed by disintegration breath, don't leave corpse */
3853         u.ugrave_arise = (type == -ZT_BREATH(ZT_DEATH)) ? -3 : NON_PM;
3854         done(DIED);
3855         return; /* lifesaved */
3856     case ZT_LIGHTNING:
3857         if (Shock_resistance) {
3858             shieldeff(sx, sy);
3859             You("aren't affected.");
3860             ugolemeffects(AD_ELEC, d(nd, 6));
3861         } else {
3862             dam = d(nd, 6);
3863             exercise(A_CON, FALSE);
3864         }
3865         if (!rn2(3))
3866             destroy_item(WAND_CLASS, AD_ELEC);
3867         if (!rn2(3))
3868             destroy_item(RING_CLASS, AD_ELEC);
3869         break;
3870     case ZT_POISON_GAS:
3871         poisoned("blast", A_DEX, "poisoned blast", 15, FALSE);
3872         break;
3873     case ZT_ACID:
3874         if (Acid_resistance) {
3875             pline_The("%s doesn't hurt.", hliquid("acid"));
3876             dam = 0;
3877         } else {
3878             pline_The("%s burns!", hliquid("acid"));
3879             dam = d(nd, 6);
3880             exercise(A_STR, FALSE);
3881         }
3882         /* using two weapons at once makes both of them more vulnerable */
3883         if (!rn2(u.twoweap ? 3 : 6))
3884             acid_damage(uwep);
3885         if (u.twoweap && !rn2(3))
3886             acid_damage(uswapwep);
3887         if (!rn2(6))
3888             erode_armor(&youmonst, ERODE_CORRODE);
3889         break;
3890     }
3891 
3892     /* Half_spell_damage protection yields half-damage for wands & spells,
3893        including hero's own ricochets; breath attacks do full damage */
3894     if (dam && Half_spell_damage && !(abstyp >= 20 && abstyp <= 29))
3895         dam = (dam + 1) / 2;
3896     losehp(dam, fltxt, KILLED_BY_AN);
3897     return;
3898 }
3899 
3900 /*
3901  * burn objects (such as scrolls and spellbooks) on floor
3902  * at position x,y; return the number of objects burned
3903  */
3904 int
burn_floor_objects(x,y,give_feedback,u_caused)3905 burn_floor_objects(x, y, give_feedback, u_caused)
3906 int x, y;
3907 boolean give_feedback; /* caller needs to decide about visibility checks */
3908 boolean u_caused;
3909 {
3910     struct obj *obj, *obj2;
3911     long i, scrquan, delquan;
3912     char buf1[BUFSZ], buf2[BUFSZ];
3913     int cnt = 0;
3914 
3915     for (obj = level.objects[x][y]; obj; obj = obj2) {
3916         obj2 = obj->nexthere;
3917         if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS
3918             || (obj->oclass == FOOD_CLASS
3919                 && obj->otyp == GLOB_OF_GREEN_SLIME)) {
3920             if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL
3921                 || obj_resists(obj, 2, 100))
3922                 continue;
3923             scrquan = obj->quan; /* number present */
3924             delquan = 0L;        /* number to destroy */
3925             for (i = scrquan; i > 0L; i--)
3926                 if (!rn2(3))
3927                     delquan++;
3928             if (delquan) {
3929                 /* save name before potential delobj() */
3930                 if (give_feedback) {
3931                     obj->quan = 1L;
3932                     Strcpy(buf1, (x == u.ux && y == u.uy)
3933                                      ? xname(obj)
3934                                      : distant_name(obj, xname));
3935                     obj->quan = 2L;
3936                     Strcpy(buf2, (x == u.ux && y == u.uy)
3937                                      ? xname(obj)
3938                                      : distant_name(obj, xname));
3939                     obj->quan = scrquan;
3940                 }
3941                 /* useupf(), which charges, only if hero caused damage */
3942                 if (u_caused)
3943                     useupf(obj, delquan);
3944                 else if (delquan < scrquan)
3945                     obj->quan -= delquan;
3946                 else
3947                     delobj(obj);
3948                 cnt += delquan;
3949                 if (give_feedback) {
3950                     if (delquan > 1L)
3951                         pline("%ld %s burn.", delquan, buf2);
3952                     else
3953                         pline("%s burns.", An(buf1));
3954                 }
3955             }
3956         }
3957     }
3958     return cnt;
3959 }
3960 
3961 /* will zap/spell/breath attack score a hit against armor class `ac'? */
3962 STATIC_OVL int
zap_hit(ac,type)3963 zap_hit(ac, type)
3964 int ac;
3965 int type; /* either hero cast spell type or 0 */
3966 {
3967     int chance = rn2(20);
3968     int spell_bonus = type ? spell_hit_bonus(type) : 0;
3969 
3970     /* small chance for naked target to avoid being hit */
3971     if (!chance)
3972         return rnd(10) < ac + spell_bonus;
3973 
3974     /* very high armor protection does not achieve invulnerability */
3975     ac = AC_VALUE(ac);
3976 
3977     return (3 - chance < ac + spell_bonus);
3978 }
3979 
3980 STATIC_OVL void
disintegrate_mon(mon,type,fltxt)3981 disintegrate_mon(mon, type, fltxt)
3982 struct monst *mon;
3983 int type; /* hero vs other */
3984 const char *fltxt;
3985 {
3986     struct obj *otmp, *otmp2, *m_amulet = mlifesaver(mon);
3987 
3988     if (canseemon(mon)) {
3989         if (!m_amulet)
3990             pline("%s is disintegrated!", Monnam(mon));
3991         else
3992             hit(fltxt, mon, "!");
3993     }
3994 
3995 /* note: worn amulet of life saving must be preserved in order to operate */
3996 #define oresist_disintegration(obj)                                       \
3997     (objects[obj->otyp].oc_oprop == DISINT_RES || obj_resists(obj, 5, 50) \
3998      || is_quest_artifact(obj) || obj == m_amulet)
3999 
4000     for (otmp = mon->minvent; otmp; otmp = otmp2) {
4001         otmp2 = otmp->nobj;
4002         if (!oresist_disintegration(otmp)) {
4003             if (otmp->owornmask) {
4004                 /* in case monster's life gets saved */
4005                 mon->misc_worn_check &= ~otmp->owornmask;
4006                 if (otmp->owornmask & W_WEP)
4007                     setmnotwielded(mon, otmp);
4008                 /* also dismounts hero if this object is steed's saddle */
4009                 update_mon_intrinsics(mon, otmp, FALSE, TRUE);
4010                 otmp->owornmask = 0L;
4011             }
4012             obj_extract_self(otmp);
4013             obfree(otmp, (struct obj *) 0);
4014         }
4015     }
4016 
4017 #undef oresist_disintegration
4018 
4019     if (type < 0)
4020         monkilled(mon, (char *) 0, -AD_RBRE);
4021     else
4022         xkilled(mon, XKILL_NOMSG | XKILL_NOCORPSE);
4023 }
4024 
4025 void
buzz(type,nd,sx,sy,dx,dy)4026 buzz(type, nd, sx, sy, dx, dy)
4027 int type, nd;
4028 xchar sx, sy;
4029 int dx, dy;
4030 {
4031     dobuzz(type, nd, sx, sy, dx, dy, TRUE);
4032 }
4033 
4034 /*
4035  * type ==   0 to   9 : you shooting a wand
4036  * type ==  10 to  19 : you casting a spell
4037  * type ==  20 to  29 : you breathing as a monster
4038  * type == -10 to -19 : monster casting spell
4039  * type == -20 to -29 : monster breathing at you
4040  * type == -30 to -39 : monster shooting a wand
4041  * called with dx = dy = 0 with vertical bolts
4042  */
4043 void
dobuzz(type,nd,sx,sy,dx,dy,say)4044 dobuzz(type, nd, sx, sy, dx, dy, say)
4045 register int type, nd;
4046 register xchar sx, sy;
4047 register int dx, dy;
4048 boolean say; /* Announce out of sight hit/miss events if true */
4049 {
4050     int range, abstype = abs(type) % 10;
4051     register xchar lsx, lsy;
4052     struct monst *mon;
4053     coord save_bhitpos;
4054     boolean shopdamage = FALSE;
4055     const char *fltxt;
4056     struct obj *otmp;
4057     int spell_type;
4058 
4059     /* if its a Hero Spell then get its SPE_TYPE */
4060     spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + abstype : 0;
4061 
4062     fltxt = flash_types[(type <= -30) ? abstype : abs(type)];
4063     if (u.uswallow) {
4064         register int tmp;
4065 
4066         if (type < 0)
4067             return;
4068         tmp = zhitm(u.ustuck, type, nd, &otmp);
4069         if (!u.ustuck)
4070             u.uswallow = 0;
4071         else
4072             pline("%s rips into %s%s", The(fltxt), mon_nam(u.ustuck),
4073                   exclam(tmp));
4074         /* Using disintegration from the inside only makes a hole... */
4075         if (tmp == MAGIC_COOKIE)
4076             u.ustuck->mhp = 0;
4077         if (DEADMONSTER(u.ustuck))
4078             killed(u.ustuck);
4079         return;
4080     }
4081     if (type < 0)
4082         newsym(u.ux, u.uy);
4083     range = rn1(7, 7);
4084     if (dx == 0 && dy == 0)
4085         range = 1;
4086     save_bhitpos = bhitpos;
4087 
4088     tmp_at(DISP_BEAM, zapdir_to_glyph(dx, dy, abstype));
4089     while (range-- > 0) {
4090         lsx = sx;
4091         sx += dx;
4092         lsy = sy;
4093         sy += dy;
4094         if (!isok(sx, sy) || levl[sx][sy].typ == STONE)
4095             goto make_bounce;
4096 
4097         mon = m_at(sx, sy);
4098         if (cansee(sx, sy)) {
4099             /* reveal/unreveal invisible monsters before tmp_at() */
4100             if (mon && !canspotmon(mon))
4101                 map_invisible(sx, sy);
4102             else if (!mon)
4103                 (void) unmap_invisible(sx, sy);
4104             if (ZAP_POS(levl[sx][sy].typ)
4105                 || (isok(lsx, lsy) && cansee(lsx, lsy)))
4106                 tmp_at(sx, sy);
4107             delay_output(); /* wait a little */
4108         }
4109 
4110         /* hit() and miss() need bhitpos to match the target */
4111         bhitpos.x = sx, bhitpos.y = sy;
4112         /* Fireballs only damage when they explode */
4113         if (type != ZT_SPELL(ZT_FIRE)) {
4114             range += zap_over_floor(sx, sy, type, &shopdamage, 0);
4115             /* zap with fire -> melt ice -> drown monster, so monster
4116                found and cached above might not be here any more */
4117             mon = m_at(sx, sy);
4118         }
4119 
4120         if (mon) {
4121             if (type == ZT_SPELL(ZT_FIRE))
4122                 break;
4123             if (type >= 0)
4124                 mon->mstrategy &= ~STRAT_WAITMASK;
4125  buzzmonst:
4126             notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y);
4127             if (zap_hit(find_mac(mon), spell_type)) {
4128                 if (mon_reflects(mon, (char *) 0)) {
4129                     if (cansee(mon->mx, mon->my)) {
4130                         hit(fltxt, mon, exclam(0));
4131                         shieldeff(mon->mx, mon->my);
4132                         (void) mon_reflects(mon,
4133                                             "But it reflects from %s %s!");
4134                     }
4135                     dx = -dx;
4136                     dy = -dy;
4137                 } else {
4138                     boolean mon_could_move = mon->mcanmove;
4139                     int tmp = zhitm(mon, type, nd, &otmp);
4140 
4141                     if (is_rider(mon->data)
4142                         && abs(type) == ZT_BREATH(ZT_DEATH)) {
4143                         if (canseemon(mon)) {
4144                             hit(fltxt, mon, ".");
4145                             pline("%s disintegrates.", Monnam(mon));
4146                             pline("%s body reintegrates before your %s!",
4147                                   s_suffix(Monnam(mon)),
4148                                   (eyecount(youmonst.data) == 1)
4149                                       ? body_part(EYE)
4150                                       : makeplural(body_part(EYE)));
4151                             pline("%s resurrects!", Monnam(mon));
4152                         }
4153                         mon->mhp = mon->mhpmax;
4154                         break; /* Out of while loop */
4155                     }
4156                     if (mon->data == &mons[PM_DEATH] && abstype == ZT_DEATH) {
4157                         if (canseemon(mon)) {
4158                             hit(fltxt, mon, ".");
4159                             pline("%s absorbs the deadly %s!", Monnam(mon),
4160                                   type == ZT_BREATH(ZT_DEATH) ? "blast"
4161                                                               : "ray");
4162                             pline("It seems even stronger than before.");
4163                         }
4164                         break; /* Out of while loop */
4165                     }
4166 
4167                     if (tmp == MAGIC_COOKIE) { /* disintegration */
4168                         disintegrate_mon(mon, type, fltxt);
4169                     } else if (DEADMONSTER(mon)) {
4170                         if (type < 0) {
4171                             /* mon has just been killed by another monster */
4172                             monkilled(mon, fltxt, AD_RBRE);
4173                         } else {
4174                             int xkflags = XKILL_GIVEMSG; /* killed(mon); */
4175 
4176                             /* killed by hero; we know 'type' isn't negative;
4177                                if it's fire, highly flammable monsters leave
4178                                no corpse; don't bother reporting that they
4179                                "burn completely" -- unnecessary verbosity */
4180                             if ((type % 10 == ZT_FIRE)
4181                                 /* paper golem or straw golem */
4182                                 && completelyburns(mon->data))
4183                                 xkflags |= XKILL_NOCORPSE;
4184                             xkilled(mon, xkflags);
4185                         }
4186                     } else {
4187                         if (!otmp) {
4188                             /* normal non-fatal hit */
4189                             if (say || canseemon(mon))
4190                                 hit(fltxt, mon, exclam(tmp));
4191                         } else {
4192                             /* some armor was destroyed; no damage done */
4193                             if (canseemon(mon))
4194                                 pline("%s %s is disintegrated!",
4195                                       s_suffix(Monnam(mon)),
4196                                       distant_name(otmp, xname));
4197                             m_useup(mon, otmp);
4198                         }
4199                         if (mon_could_move && !mon->mcanmove) /* ZT_SLEEP */
4200                             slept_monst(mon);
4201                     }
4202                 }
4203                 range -= 2;
4204             } else {
4205                 if (say || canseemon(mon))
4206                     miss(fltxt, mon);
4207             }
4208         } else if (sx == u.ux && sy == u.uy && range >= 0) {
4209             nomul(0);
4210             if (u.usteed && !rn2(3) && !mon_reflects(u.usteed, (char *) 0)) {
4211                 mon = u.usteed;
4212                 goto buzzmonst;
4213             } else if (zap_hit((int) u.uac, 0)) {
4214                 range -= 2;
4215                 pline("%s hits you!", The(fltxt));
4216                 if (Reflecting) {
4217                     if (!Blind) {
4218                         (void) ureflects("But %s reflects from your %s!",
4219                                          "it");
4220                     } else
4221                         pline("For some reason you are not affected.");
4222                     dx = -dx;
4223                     dy = -dy;
4224                     shieldeff(sx, sy);
4225                 } else {
4226                     zhitu(type, nd, fltxt, sx, sy);
4227                 }
4228             } else if (!Blind) {
4229                 pline("%s whizzes by you!", The(fltxt));
4230             } else if (abstype == ZT_LIGHTNING) {
4231                 Your("%s tingles.", body_part(ARM));
4232             }
4233             if (abstype == ZT_LIGHTNING)
4234                 (void) flashburn((long) d(nd, 50));
4235             stop_occupation();
4236             nomul(0);
4237         }
4238 
4239         if (!ZAP_POS(levl[sx][sy].typ)
4240             || (closed_door(sx, sy) && range >= 0)) {
4241             int bounce, bchance;
4242             uchar rmn;
4243             boolean fireball;
4244 
4245  make_bounce:
4246             bchance = (levl[sx][sy].typ == STONE) ? 10
4247                 : (In_mines(&u.uz) && IS_WALL(levl[sx][sy].typ)) ? 20
4248                 : 75;
4249             bounce = 0;
4250             fireball = (type == ZT_SPELL(ZT_FIRE));
4251             if ((--range > 0 && isok(lsx, lsy) && cansee(lsx, lsy))
4252                 || fireball) {
4253                 if (Is_airlevel(&u.uz)) { /* nothing to bounce off of */
4254                     pline_The("%s vanishes into the aether!", fltxt);
4255                     if (fireball)
4256                         type = ZT_WAND(ZT_FIRE); /* skip pending fireball */
4257                     break;
4258                 } else if (fireball) {
4259                     sx = lsx;
4260                     sy = lsy;
4261                     break; /* fireballs explode before the obstacle */
4262                 } else
4263                     pline_The("%s bounces!", fltxt);
4264             }
4265             if (!dx || !dy || !rn2(bchance)) {
4266                 dx = -dx;
4267                 dy = -dy;
4268             } else {
4269                 if (isok(sx, lsy) && ZAP_POS(rmn = levl[sx][lsy].typ)
4270                     && !closed_door(sx, lsy)
4271                     && (IS_ROOM(rmn) || (isok(sx + dx, lsy)
4272                                          && ZAP_POS(levl[sx + dx][lsy].typ))))
4273                     bounce = 1;
4274                 if (isok(lsx, sy) && ZAP_POS(rmn = levl[lsx][sy].typ)
4275                     && !closed_door(lsx, sy)
4276                     && (IS_ROOM(rmn) || (isok(lsx, sy + dy)
4277                                          && ZAP_POS(levl[lsx][sy + dy].typ))))
4278                     if (!bounce || rn2(2))
4279                         bounce = 2;
4280 
4281                 switch (bounce) {
4282                 case 0:
4283                     dx = -dx;
4284                     /*FALLTHRU*/
4285                 case 1:
4286                     dy = -dy;
4287                     break;
4288                 case 2:
4289                     dx = -dx;
4290                     break;
4291                 }
4292                 tmp_at(DISP_CHANGE, zapdir_to_glyph(dx, dy, abstype));
4293             }
4294         }
4295     }
4296     tmp_at(DISP_END, 0);
4297     if (type == ZT_SPELL(ZT_FIRE))
4298         explode(sx, sy, type, d(12, 6), 0, EXPL_FIERY);
4299     if (shopdamage)
4300         pay_for_damage(abstype == ZT_FIRE
4301                           ? "burn away"
4302                           : abstype == ZT_COLD
4303                              ? "shatter"
4304                              /* "damage" indicates wall rather than door */
4305                              : abstype == ZT_ACID
4306                                 ? "damage"
4307                                 : abstype == ZT_DEATH
4308                                    ? "disintegrate"
4309                                    : "destroy",
4310                        FALSE);
4311     bhitpos = save_bhitpos;
4312 }
4313 
4314 void
melt_ice(x,y,msg)4315 melt_ice(x, y, msg)
4316 xchar x, y;
4317 const char *msg;
4318 {
4319     struct rm *lev = &levl[x][y];
4320     struct obj *otmp;
4321     struct monst *mtmp;
4322 
4323     if (!msg)
4324         msg = "The ice crackles and melts.";
4325     if (lev->typ == DRAWBRIDGE_UP || lev->typ == DRAWBRIDGE_DOWN) {
4326         lev->drawbridgemask &= ~DB_ICE; /* revert to DB_MOAT */
4327     } else { /* lev->typ == ICE */
4328 #ifdef STUPID
4329         if (lev->icedpool == ICED_POOL)
4330             lev->typ = POOL;
4331         else
4332             lev->typ = MOAT;
4333 #else
4334         lev->typ = (lev->icedpool == ICED_POOL ? POOL : MOAT);
4335 #endif
4336         lev->icedpool = 0;
4337     }
4338     spot_stop_timers(x, y, MELT_ICE_AWAY); /* no more ice to melt away */
4339     obj_ice_effects(x, y, FALSE);
4340     unearth_objs(x, y);
4341     if (Underwater)
4342         vision_recalc(1);
4343     newsym(x, y);
4344     if (cansee(x, y))
4345         Norep("%s", msg);
4346     if ((otmp = sobj_at(BOULDER, x, y)) != 0) {
4347         if (cansee(x, y))
4348             pline("%s settles...", An(xname(otmp)));
4349         do {
4350             obj_extract_self(otmp); /* boulder isn't being pushed */
4351             if (!boulder_hits_pool(otmp, x, y, FALSE))
4352                 impossible("melt_ice: no pool?");
4353             /* try again if there's another boulder and pool didn't fill */
4354         } while (is_pool(x, y) && (otmp = sobj_at(BOULDER, x, y)) != 0);
4355         newsym(x, y);
4356     }
4357     if (x == u.ux && y == u.uy)
4358         spoteffects(TRUE); /* possibly drown, notice objects */
4359     else if (is_pool(x, y) && (mtmp = m_at(x, y)) != 0)
4360         (void) minliquid(mtmp);
4361 }
4362 
4363 #define MIN_ICE_TIME 50
4364 #define MAX_ICE_TIME 2000
4365 /*
4366  * Usually start a melt_ice timer; sometimes the ice will become
4367  * permanent instead.
4368  */
4369 void
start_melt_ice_timeout(x,y,min_time)4370 start_melt_ice_timeout(x, y, min_time)
4371 xchar x, y;
4372 long min_time; /* <x,y>'s old melt timeout (deleted by time we get here) */
4373 {
4374     int when;
4375     long where;
4376 
4377     when = (int) min_time;
4378     if (when < MIN_ICE_TIME - 1)
4379         when = MIN_ICE_TIME - 1;
4380 
4381     /* random timeout; surrounding ice locations ought to be a factor... */
4382     while (++when <= MAX_ICE_TIME)
4383         if (!rn2((MAX_ICE_TIME - when) + MIN_ICE_TIME))
4384             break;
4385 
4386     /* if we're within MAX_ICE_TIME, install a melt timer;
4387        otherwise, omit it to leave this ice permanent */
4388     if (when <= MAX_ICE_TIME) {
4389         where = ((long) x << 16) | (long) y;
4390         (void) start_timer((long) when, TIMER_LEVEL, MELT_ICE_AWAY,
4391                            long_to_any(where));
4392     }
4393 }
4394 #undef MIN_ICE_TIME
4395 #undef MAX_ICE_TIME
4396 
4397 /*
4398  * Called when ice has melted completely away.
4399  */
4400 void
melt_ice_away(arg,timeout)4401 melt_ice_away(arg, timeout)
4402 anything *arg;
4403 long timeout UNUSED;
4404 {
4405     xchar x, y;
4406     long where = arg->a_long;
4407     boolean save_mon_moving = context.mon_moving; /* will be False */
4408 
4409     /* melt_ice -> minliquid -> mondead|xkilled shouldn't credit/blame hero */
4410     context.mon_moving = TRUE; /* hero isn't causing this ice to melt */
4411     y = (xchar) (where & 0xFFFF);
4412     x = (xchar) ((where >> 16) & 0xFFFF);
4413     /* melt_ice does newsym when appropriate */
4414     melt_ice(x, y, "Some ice melts away.");
4415     context.mon_moving = save_mon_moving;
4416 }
4417 
4418 /* Burn floor scrolls, evaporate pools, etc... in a single square.
4419  * Used both for normal bolts of fire, cold, etc... and for fireballs.
4420  * Sets shopdamage to TRUE if a shop door is destroyed, and returns the
4421  * amount by which range is reduced (the latter is just ignored by fireballs)
4422  */
4423 int
zap_over_floor(x,y,type,shopdamage,exploding_wand_typ)4424 zap_over_floor(x, y, type, shopdamage, exploding_wand_typ)
4425 xchar x, y;
4426 int type;
4427 boolean *shopdamage;
4428 short exploding_wand_typ;
4429 {
4430     const char *zapverb;
4431     struct monst *mon;
4432     struct trap *t;
4433     struct rm *lev = &levl[x][y];
4434     boolean see_it = cansee(x, y), yourzap;
4435     int rangemod = 0, abstype = abs(type) % 10;
4436 
4437     switch (abstype) {
4438     case ZT_FIRE:
4439         t = t_at(x, y);
4440         if (t && t->ttyp == WEB) {
4441             /* a burning web is too flimsy to notice if you can't see it */
4442             if (see_it)
4443                 Norep("A web bursts into flames!");
4444             (void) delfloortrap(t);
4445             if (see_it)
4446                 newsym(x, y);
4447         }
4448         if (is_ice(x, y)) {
4449             melt_ice(x, y, (char *) 0);
4450         } else if (is_pool(x, y)) {
4451             const char *msgtxt = (!Deaf)
4452                                      ? "You hear hissing gas." /* Deaf-aware */
4453                                      : (type >= 0)
4454                                          ? "That seemed remarkably uneventful."
4455                                          : (const char *) 0;
4456 
4457             if (lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP */
4458                 if (see_it)
4459                     msgtxt = "Some water evaporates.";
4460             } else {
4461                 rangemod -= 3;
4462                 lev->typ = ROOM, lev->flags = 0;
4463                 t = maketrap(x, y, PIT);
4464                 if (t)
4465                     t->tseen = 1;
4466                 if (see_it)
4467                     msgtxt = "The water evaporates.";
4468             }
4469             if (msgtxt)
4470                 Norep("%s", msgtxt);
4471             if (lev->typ == ROOM)
4472                 newsym(x, y);
4473         } else if (IS_FOUNTAIN(lev->typ)) {
4474             if (see_it)
4475                 pline("Steam billows from the fountain.");
4476             rangemod -= 1;
4477             dryup(x, y, type > 0);
4478         }
4479         break; /* ZT_FIRE */
4480 
4481     case ZT_COLD:
4482         if (is_pool(x, y) || is_lava(x, y)) {
4483             boolean lava = is_lava(x, y),
4484                     moat = is_moat(x, y);
4485 
4486             if (lev->typ == WATER) {
4487                 /* For now, don't let WATER freeze. */
4488                 if (see_it)
4489                     pline_The("%s freezes for a moment.", hliquid("water"));
4490                 else
4491                     You_hear("a soft crackling.");
4492                 rangemod -= 1000; /* stop */
4493             } else {
4494                 char buf[BUFSZ];
4495 
4496                 Strcpy(buf, waterbody_name(x, y)); /* for MOAT */
4497                 rangemod -= 3;
4498                 if (lev->typ == DRAWBRIDGE_UP) {
4499                     lev->drawbridgemask &= ~DB_UNDER; /* clear lava */
4500                     lev->drawbridgemask |= (lava ? DB_FLOOR : DB_ICE);
4501                 } else {
4502                     lev->icedpool = lava ? 0
4503                                          : (lev->typ == POOL) ? ICED_POOL
4504                                                               : ICED_MOAT;
4505                     lev->typ = lava ? ROOM : ICE;
4506                 }
4507                 bury_objs(x, y);
4508                 if (see_it) {
4509                     if (lava)
4510                         Norep("The %s cools and solidifies.", hliquid("lava"));
4511                     else if (moat)
4512                         Norep("The %s is bridged with ice!", buf);
4513                     else
4514                         Norep("The %s freezes.", hliquid("water"));
4515                     newsym(x, y);
4516                 } else if (!lava)
4517                     You_hear("a crackling sound.");
4518 
4519                 if (x == u.ux && y == u.uy) {
4520                     if (u.uinwater) { /* not just `if (Underwater)' */
4521                         /* leave the no longer existent water */
4522                         u.uinwater = 0;
4523                         u.uundetected = 0;
4524                         docrt();
4525                         vision_full_recalc = 1;
4526                     } else if (u.utrap && u.utraptype == TT_LAVA) {
4527                         if (Passes_walls) {
4528                             You("pass through the now-solid rock.");
4529                             reset_utrap(TRUE);
4530                         } else {
4531                             set_utrap(rn1(50, 20), TT_INFLOOR);
4532                             You("are firmly stuck in the cooling rock.");
4533                         }
4534                     }
4535                 } else if ((mon = m_at(x, y)) != 0) {
4536                     /* probably ought to do some hefty damage to any
4537                        non-ice creature caught in freezing water;
4538                        at a minimum, eels are forced out of hiding */
4539                     if (is_swimmer(mon->data) && mon->mundetected) {
4540                         mon->mundetected = 0;
4541                         newsym(x, y);
4542                     }
4543                 }
4544                 if (!lava) {
4545                     start_melt_ice_timeout(x, y, 0L);
4546                     obj_ice_effects(x, y, TRUE);
4547                 }
4548             } /* ?WATER */
4549 
4550         } else if (is_ice(x, y)) {
4551             long melt_time;
4552 
4553             /* Already ice here, so just firm it up. */
4554             /* Now ensure that only ice that is already timed is affected */
4555             if ((melt_time = spot_time_left(x, y, MELT_ICE_AWAY)) != 0L) {
4556                 spot_stop_timers(x, y, MELT_ICE_AWAY);
4557                 start_melt_ice_timeout(x, y, melt_time);
4558             }
4559         }
4560         break; /* ZT_COLD */
4561 
4562     case ZT_POISON_GAS:
4563         (void) create_gas_cloud(x, y, 1, 8);
4564         break;
4565 
4566     case ZT_ACID:
4567         if (lev->typ == IRONBARS) {
4568             if ((lev->wall_info & W_NONDIGGABLE) != 0) {
4569                 if (see_it)
4570                     Norep("The %s corrode somewhat but remain intact.",
4571                           defsyms[S_bars].explanation);
4572                 /* but nothing actually happens... */
4573             } else {
4574                 rangemod -= 3;
4575                 if (see_it)
4576                     Norep("The %s melt.", defsyms[S_bars].explanation);
4577                 if (*in_rooms(x, y, SHOPBASE)) {
4578                     /* in case we ever have a shop bounded by bars */
4579                     lev->typ = ROOM, lev->flags = 0;
4580                     if (see_it)
4581                         newsym(x, y);
4582                     add_damage(x, y, (type >= 0) ? SHOP_BARS_COST : 0L);
4583                     if (type >= 0)
4584                         *shopdamage = TRUE;
4585                 } else {
4586                     lev->typ = DOOR, lev->doormask = D_NODOOR;
4587                     if (see_it)
4588                         newsym(x, y);
4589                 }
4590             }
4591         }
4592         break; /* ZT_ACID */
4593 
4594     default:
4595         break;
4596     }
4597 
4598     /* set up zap text for possible door feedback; for exploding wand, we
4599        want "the blast" rather than "your blast" even if hero caused it */
4600     yourzap = (type >= 0 && !exploding_wand_typ);
4601     zapverb = "blast"; /* breath attack or wand explosion */
4602     if (!exploding_wand_typ) {
4603         if (abs(type) < ZT_SPELL(0))
4604             zapverb = "bolt"; /* wand zap */
4605         else if (abs(type) < ZT_BREATH(0))
4606             zapverb = "spell";
4607     }
4608 
4609     /* secret door gets revealed, converted into regular door */
4610     if (levl[x][y].typ == SDOOR) {
4611         cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
4612         /* target spot will now pass closed_door() test below
4613            (except on rogue level) */
4614         newsym(x, y);
4615         if (see_it)
4616             pline("%s %s reveals a secret door.",
4617                   yourzap ? "Your" : "The", zapverb);
4618         else if (Is_rogue_level(&u.uz))
4619             draft_message(FALSE); /* "You feel a draft." (open doorway) */
4620     }
4621 
4622     /* regular door absorbs remaining zap range, possibly gets destroyed */
4623     if (closed_door(x, y)) {
4624         int new_doormask = -1;
4625         const char *see_txt = 0, *sense_txt = 0, *hear_txt = 0;
4626 
4627         rangemod = -1000;
4628         switch (abstype) {
4629         case ZT_FIRE:
4630             new_doormask = D_NODOOR;
4631             see_txt = "The door is consumed in flames!";
4632             sense_txt = "smell smoke.";
4633             break;
4634         case ZT_COLD:
4635             new_doormask = D_NODOOR;
4636             see_txt = "The door freezes and shatters!";
4637             sense_txt = "feel cold.";
4638             break;
4639         case ZT_DEATH:
4640             /* death spells/wands don't disintegrate */
4641             if (abs(type) != ZT_BREATH(ZT_DEATH))
4642                 goto def_case;
4643             new_doormask = D_NODOOR;
4644             see_txt = "The door disintegrates!";
4645             hear_txt = "crashing wood.";
4646             break;
4647         case ZT_LIGHTNING:
4648             new_doormask = D_BROKEN;
4649             see_txt = "The door splinters!";
4650             hear_txt = "crackling.";
4651             break;
4652         default:
4653  def_case:
4654             if (exploding_wand_typ > 0) {
4655                 /* Magical explosion from misc exploding wand */
4656                 if (exploding_wand_typ == WAN_STRIKING) {
4657                     new_doormask = D_BROKEN;
4658                     see_txt = "The door crashes open!";
4659                     sense_txt = "feel a burst of cool air.";
4660                     break;
4661                 }
4662             }
4663             if (see_it) {
4664                 /* "the door absorbs the blast" would be
4665                    inaccurate for an exploding wand since
4666                    other adjacent locations still get hit */
4667                 if (exploding_wand_typ)
4668                     pline_The("door remains intact.");
4669                 else
4670                     pline_The("door absorbs %s %s!", yourzap ? "your" : "the",
4671                               zapverb);
4672             } else
4673                 You_feel("vibrations.");
4674             break;
4675         }
4676         if (new_doormask >= 0) { /* door gets broken */
4677             if (*in_rooms(x, y, SHOPBASE)) {
4678                 if (type >= 0) {
4679                     add_damage(x, y, SHOP_DOOR_COST);
4680                     *shopdamage = TRUE;
4681                 } else /* caused by monster */
4682                     add_damage(x, y, 0L);
4683             }
4684             lev->doormask = new_doormask;
4685             unblock_point(x, y); /* vision */
4686             if (see_it) {
4687                 pline1(see_txt);
4688                 newsym(x, y);
4689             } else if (sense_txt) {
4690                 You1(sense_txt);
4691             } else if (hear_txt)
4692                 You_hear1(hear_txt);
4693             if (picking_at(x, y)) {
4694                 stop_occupation();
4695                 reset_pick();
4696             }
4697         }
4698     }
4699 
4700     if (OBJ_AT(x, y) && abstype == ZT_FIRE)
4701         if (burn_floor_objects(x, y, FALSE, type > 0) && couldsee(x, y)) {
4702             newsym(x, y);
4703             You("%s of smoke.", !Blind ? "see a puff" : "smell a whiff");
4704         }
4705     if ((mon = m_at(x, y)) != 0) {
4706         wakeup(mon, FALSE);
4707         if (type >= 0) {
4708             setmangry(mon, TRUE);
4709             if (mon->ispriest && *in_rooms(mon->mx, mon->my, TEMPLE))
4710                 ghod_hitsu(mon);
4711             if (mon->isshk && !*u.ushops)
4712                 hot_pursuit(mon);
4713         }
4714     }
4715     return rangemod;
4716 }
4717 
4718 /* fractured by pick-axe or wand of striking */
4719 void
fracture_rock(obj)4720 fracture_rock(obj)
4721 register struct obj *obj; /* no texts here! */
4722 {
4723     xchar x, y;
4724     boolean by_you = !context.mon_moving;
4725 
4726     if (by_you && get_obj_location(obj, &x, &y, 0) && costly_spot(x, y)) {
4727         struct monst *shkp = 0;
4728         char objroom = *in_rooms(x, y, SHOPBASE);
4729 
4730         if (billable(&shkp, obj, objroom, FALSE)) {
4731             /* shop message says "you owe <shk> <$> for it!" so we need
4732                to precede that with a message explaining what "it" is */
4733             You("fracture %s %s.", s_suffix(shkname(shkp)), xname(obj));
4734             breakobj(obj, x, y, TRUE, FALSE); /* charges for shop goods */
4735         }
4736     }
4737     if (by_you && obj->otyp == BOULDER)
4738         sokoban_guilt();
4739 
4740     obj->otyp = ROCK;
4741     obj->oclass = GEM_CLASS;
4742     obj->quan = (long) rn1(60, 7);
4743     obj->owt = weight(obj);
4744     obj->dknown = obj->bknown = obj->rknown = 0;
4745     obj->known = objects[obj->otyp].oc_uses_known ? 0 : 1;
4746     dealloc_oextra(obj);
4747 
4748     if (obj->where == OBJ_FLOOR) {
4749         obj_extract_self(obj); /* move rocks back on top */
4750         place_object(obj, obj->ox, obj->oy);
4751         if (!does_block(obj->ox, obj->oy, &levl[obj->ox][obj->oy]))
4752             unblock_point(obj->ox, obj->oy);
4753         if (cansee(obj->ox, obj->oy))
4754             newsym(obj->ox, obj->oy);
4755     }
4756 }
4757 
4758 /* handle statue hit by striking/force bolt/pick-axe */
4759 boolean
break_statue(obj)4760 break_statue(obj)
4761 register struct obj *obj;
4762 {
4763     /* [obj is assumed to be on floor, so no get_obj_location() needed] */
4764     struct trap *trap = t_at(obj->ox, obj->oy);
4765     struct obj *item;
4766     boolean by_you = !context.mon_moving;
4767 
4768     if (trap && trap->ttyp == STATUE_TRAP
4769         && activate_statue_trap(trap, obj->ox, obj->oy, TRUE))
4770         return FALSE;
4771     /* drop any objects contained inside the statue */
4772     while ((item = obj->cobj) != 0) {
4773         obj_extract_self(item);
4774         place_object(item, obj->ox, obj->oy);
4775     }
4776     if (by_you && Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC)) {
4777         You_feel("guilty about damaging such a historic statue.");
4778         adjalign(-1);
4779     }
4780     obj->spe = 0;
4781     fracture_rock(obj);
4782     return TRUE;
4783 }
4784 
4785 /*
4786  * destroy_strings[dindx][0:singular,1:plural,2:killer_reason]
4787  *      [0] freezing potion
4788  *      [1] boiling potion other than oil
4789  *      [2] boiling potion of oil
4790  *      [3] burning scroll
4791  *      [4] burning spellbook
4792  *      [5] shocked ring
4793  *      [6] shocked wand
4794  * (books, rings, and wands don't stack so don't need plural form;
4795  *  crumbling ring doesn't do damage so doesn't need killer reason)
4796  */
4797 const char *const destroy_strings[][3] = {
4798     /* also used in trap.c */
4799     { "freezes and shatters", "freeze and shatter", "shattered potion" },
4800     { "boils and explodes", "boil and explode", "boiling potion" },
4801     { "ignites and explodes", "ignite and explode", "exploding potion" },
4802     { "catches fire and burns", "catch fire and burn", "burning scroll" },
4803     { "catches fire and burns", "", "burning book" },
4804     { "turns to dust and vanishes", "", "" },
4805     { "breaks apart and explodes", "", "exploding wand" },
4806 };
4807 
4808 /* guts of destroy_item(), which ought to be called maybe_destroy_items();
4809    caller must decide whether obj is eligible */
4810 STATIC_OVL void
destroy_one_item(obj,osym,dmgtyp)4811 destroy_one_item(obj, osym, dmgtyp)
4812 struct obj *obj;
4813 int osym, dmgtyp;
4814 {
4815     long i, cnt, quan;
4816     int dmg, xresist, skip, dindx;
4817     const char *mult;
4818     boolean physical_damage;
4819 
4820     physical_damage = FALSE;
4821     xresist = skip = 0;
4822     /* lint suppression */
4823     dmg = dindx = 0;
4824     quan = 0L;
4825 
4826     switch (dmgtyp) {
4827     case AD_COLD:
4828         if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
4829             quan = obj->quan;
4830             dindx = 0;
4831             dmg = rnd(4);
4832         } else
4833             skip++;
4834         break;
4835     case AD_FIRE:
4836         xresist = (Fire_resistance && obj->oclass != POTION_CLASS
4837                    && obj->otyp != GLOB_OF_GREEN_SLIME);
4838         if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
4839             skip++;
4840         if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
4841             skip++;
4842             if (!Blind)
4843                 pline("%s glows a strange %s, but remains intact.",
4844                       The(xname(obj)), hcolor("dark red"));
4845         }
4846         quan = obj->quan;
4847         switch (osym) {
4848         case POTION_CLASS:
4849             dindx = (obj->otyp != POT_OIL) ? 1 : 2;
4850             dmg = rnd(6);
4851             break;
4852         case SCROLL_CLASS:
4853             dindx = 3;
4854             dmg = 1;
4855             break;
4856         case SPBOOK_CLASS:
4857             dindx = 4;
4858             dmg = 1;
4859             break;
4860         case FOOD_CLASS:
4861             if (obj->otyp == GLOB_OF_GREEN_SLIME) {
4862                 dindx = 1; /* boil and explode */
4863                 dmg = (obj->owt + 19) / 20;
4864             } else {
4865                 skip++;
4866             }
4867             break;
4868         default:
4869             skip++;
4870             break;
4871         }
4872         break;
4873     case AD_ELEC:
4874         xresist = (Shock_resistance && obj->oclass != RING_CLASS);
4875         quan = obj->quan;
4876         switch (osym) {
4877         case RING_CLASS:
4878             if (obj->otyp == RIN_SHOCK_RESISTANCE) {
4879                 skip++;
4880                 break;
4881             }
4882             dindx = 5;
4883             dmg = 0;
4884             break;
4885         case WAND_CLASS:
4886             if (obj->otyp == WAN_LIGHTNING) {
4887                 skip++;
4888                 break;
4889             }
4890 #if 0
4891             if (obj == current_wand) {  skip++;  break;  }
4892 #endif
4893             dindx = 6;
4894             dmg = rnd(10);
4895             break;
4896         default:
4897             skip++;
4898             break;
4899         }
4900         break;
4901     default:
4902         skip++;
4903         break;
4904     }
4905 
4906     if (!skip) {
4907         if (obj->in_use)
4908             --quan; /* one will be used up elsewhere */
4909         for (i = cnt = 0L; i < quan; i++)
4910             if (!rn2(3))
4911                 cnt++;
4912 
4913         if (!cnt)
4914             return;
4915         mult = (cnt == 1L)
4916                 ? ((quan == 1L) ? "Your"                         /* 1 of 1 */
4917                                 : "One of your")                 /* 1 of N */
4918                 : ((cnt < quan) ? "Some of your"                 /* n of N */
4919                                 : (quan == 2L) ? "Both of your"  /* 2 of 2 */
4920                                                : "All of your"); /* N of N */
4921         pline("%s %s %s!", mult, xname(obj),
4922               destroy_strings[dindx][(cnt > 1L)]);
4923         if (osym == POTION_CLASS && dmgtyp != AD_COLD) {
4924             if (!breathless(youmonst.data) || haseyes(youmonst.data))
4925                 potionbreathe(obj);
4926         }
4927         if (obj->owornmask) {
4928             if (obj->owornmask & W_RING) /* ring being worn */
4929                 Ring_gone(obj);
4930             else
4931                 setnotworn(obj);
4932         }
4933         if (obj == current_wand)
4934             current_wand = 0; /* destroyed */
4935         for (i = 0; i < cnt; i++)
4936             useup(obj);
4937         if (dmg) {
4938             if (xresist) {
4939                 You("aren't hurt!");
4940             } else {
4941                 const char *how = destroy_strings[dindx][2];
4942                 boolean one = (cnt == 1L);
4943 
4944                 if (dmgtyp == AD_FIRE && osym == FOOD_CLASS)
4945                     how = "exploding glob of slime";
4946                 if (physical_damage)
4947                     dmg = Maybe_Half_Phys(dmg);
4948                 losehp(dmg, one ? how : (const char *) makeplural(how),
4949                        one ? KILLED_BY_AN : KILLED_BY);
4950                 exercise(A_STR, FALSE);
4951             }
4952         }
4953     }
4954 }
4955 
4956 /* target items of specified class for possible destruction */
4957 void
destroy_item(osym,dmgtyp)4958 destroy_item(osym, dmgtyp)
4959 int osym, dmgtyp;
4960 {
4961     register struct obj *obj;
4962     int i, deferral_indx = 0;
4963     /* 1+52+1: try to handle a full inventory; it doesn't matter if
4964       inventory actually has more, even if everything should be deferred */
4965     unsigned short deferrals[1 + 52 + 1]; /* +1: gold, overflow */
4966 
4967     (void) memset((genericptr_t) deferrals, 0, sizeof deferrals);
4968     /*
4969      * Sometimes destroying an item can change inventory aside from
4970      * the item itself (cited case was a potion of unholy water; when
4971      * boiled, potionbreathe() caused hero to transform into were-beast
4972      * form and that resulted in dropping or destroying some worn armor).
4973      *
4974      * Unlike other uses of the object bybass mechanism, destroy_item()
4975      * can be called multiple times for the same event.  So we have to
4976      * explicitly clear it before each use and hope no other section of
4977      * code expects it to retain previous value.
4978      *
4979      * Destruction of a ring of levitation or form change which pushes
4980      * off levitation boots could drop hero onto a fire trap that
4981      * could destroy other items and we'll get called recursively.  Or
4982      * onto a trap which transports hero elsewhere, which won't disrupt
4983      * traversal but could yield message sequencing issues.  So we
4984      * defer handling such things until after rest of inventory has
4985      * been processed.  If some other combination of items and events
4986      * triggers a recursive call, rest of inventory after the triggering
4987      * item will be skipped by the outer call since the inner one will
4988      * have set the bypass bits of the whole list.
4989      *
4990      * [Unfortunately, death while poly'd into flyer and subsequent
4991      * rehumanization could also drop hero onto a trap, and there's no
4992      * straightforward way to defer that.  Things could be improved by
4993      * redoing this to use two passes, first to collect a list or array
4994      * of o_id and quantity of what is targetted for destruction,
4995      * second pass to handle the destruction.]
4996      */
4997     bypass_objlist(invent, FALSE); /* clear bypass bit for invent */
4998 
4999     while ((obj = nxt_unbypassed_obj(invent)) != 0) {
5000         if (obj->oclass != osym)
5001             continue; /* test only objs of type osym */
5002         if (obj->oartifact)
5003             continue; /* don't destroy artifacts */
5004         if (obj->in_use && obj->quan == 1L)
5005             continue; /* not available */
5006 
5007         /* if loss of this item might dump us onto a trap, hold off
5008            until later because potential recursive destroy_item() will
5009            result in setting bypass bits on whole chain--we would skip
5010            the rest as already processed once control returns here */
5011         if (deferral_indx < SIZE(deferrals)
5012             && ((obj->owornmask != 0L
5013                  && (objects[obj->otyp].oc_oprop == LEVITATION
5014                      || objects[obj->otyp].oc_oprop == FLYING))
5015                 /* destroyed wands and potions of polymorph don't trigger
5016                    polymorph so don't need to be deferred */
5017                 || (obj->otyp == POT_WATER && u.ulycn >= LOW_PM
5018                     && (Upolyd ? obj->blessed : obj->cursed)))) {
5019             deferrals[deferral_indx++] = obj->o_id;
5020             continue;
5021         }
5022         /* obj is eligible; maybe destroy it */
5023         destroy_one_item(obj, osym, dmgtyp);
5024     }
5025     /* if we saved some items for later (most likely just a worn ring
5026        of levitation) and they're still in inventory, handle them now */
5027     for (i = 0; i < deferral_indx; ++i) {
5028         /* note: obj->nobj is only referenced when obj is skipped;
5029            having obj be dropped or destroyed won't affect traversal */
5030         for (obj = invent; obj; obj = obj->nobj)
5031             if (obj->o_id == deferrals[i]) {
5032                 destroy_one_item(obj, osym, dmgtyp);
5033                 break;
5034             }
5035     }
5036     return;
5037 }
5038 
5039 int
destroy_mitem(mtmp,osym,dmgtyp)5040 destroy_mitem(mtmp, osym, dmgtyp)
5041 struct monst *mtmp;
5042 int osym, dmgtyp;
5043 {
5044     struct obj *obj;
5045     int skip, tmp = 0;
5046     long i, cnt, quan;
5047     int dindx;
5048     boolean vis;
5049 
5050     if (mtmp == &youmonst) { /* this simplifies artifact_hit() */
5051         destroy_item(osym, dmgtyp);
5052         return 0; /* arbitrary; value doesn't matter to artifact_hit() */
5053     }
5054 
5055     vis = canseemon(mtmp);
5056 
5057     /* see destroy_item(); object destruction could disrupt inventory list */
5058     bypass_objlist(mtmp->minvent, FALSE); /* clear bypass bit for minvent */
5059 
5060     while ((obj = nxt_unbypassed_obj(mtmp->minvent)) != 0) {
5061         if (obj->oclass != osym)
5062             continue; /* test only objs of type osym */
5063         skip = 0;
5064         quan = 0L;
5065         dindx = 0;
5066 
5067         switch (dmgtyp) {
5068         case AD_COLD:
5069             if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
5070                 quan = obj->quan;
5071                 dindx = 0;
5072                 tmp++;
5073             } else
5074                 skip++;
5075             break;
5076         case AD_FIRE:
5077             if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
5078                 skip++;
5079             if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
5080                 skip++;
5081                 if (vis)
5082                     pline("%s glows a strange %s, but remains intact.",
5083                           The(distant_name(obj, xname)), hcolor("dark red"));
5084             }
5085             quan = obj->quan;
5086             switch (osym) {
5087             case POTION_CLASS:
5088                 dindx = (obj->otyp != POT_OIL) ? 1 : 2;
5089                 tmp++;
5090                 break;
5091             case SCROLL_CLASS:
5092                 dindx = 3;
5093                 tmp++;
5094                 break;
5095             case SPBOOK_CLASS:
5096                 dindx = 4;
5097                 tmp++;
5098                 break;
5099             case FOOD_CLASS:
5100                 if (obj->otyp == GLOB_OF_GREEN_SLIME) {
5101                     dindx = 1; /* boil and explode */
5102                     tmp += (obj->owt + 19) / 20;
5103                 } else {
5104                     skip++;
5105                 }
5106                 break;
5107             default:
5108                 skip++;
5109                 break;
5110             }
5111             break;
5112         case AD_ELEC:
5113             quan = obj->quan;
5114             switch (osym) {
5115             case RING_CLASS:
5116                 if (obj->otyp == RIN_SHOCK_RESISTANCE) {
5117                     skip++;
5118                     break;
5119                 }
5120                 dindx = 5;
5121                 break;
5122             case WAND_CLASS:
5123                 if (obj->otyp == WAN_LIGHTNING) {
5124                     skip++;
5125                     break;
5126                 }
5127                 dindx = 6;
5128                 tmp++;
5129                 break;
5130             default:
5131                 skip++;
5132                 break;
5133             }
5134             break;
5135         default:
5136             skip++;
5137             break;
5138         }
5139         if (!skip) {
5140             for (i = cnt = 0L; i < quan; i++)
5141                 if (!rn2(3))
5142                     cnt++;
5143 
5144             if (!cnt)
5145                 continue;
5146             if (vis)
5147                 pline("%s%s %s!",
5148                       (cnt == obj->quan) ? "" : (cnt > 1L) ? "Some of "
5149                                                            : "One of ",
5150                       (cnt == obj->quan) ? Yname2(obj) : yname(obj),
5151                       destroy_strings[dindx][(cnt > 1L)]);
5152             for (i = 0; i < cnt; i++)
5153                 m_useup(mtmp, obj);
5154         }
5155     }
5156     return tmp;
5157 }
5158 
5159 int
resist(mtmp,oclass,damage,tell)5160 resist(mtmp, oclass, damage, tell)
5161 struct monst *mtmp;
5162 char oclass;
5163 int damage, tell;
5164 {
5165     int resisted;
5166     int alev, dlev;
5167 
5168     /* fake players always pass resistance test against Conflict
5169        (this doesn't guarantee that they're never affected by it) */
5170     if (oclass == RING_CLASS && !damage && !tell && is_mplayer(mtmp->data))
5171         return 1;
5172 
5173     /* attack level */
5174     switch (oclass) {
5175     case WAND_CLASS:
5176         alev = 12;
5177         break;
5178     case TOOL_CLASS:
5179         alev = 10;
5180         break; /* instrument */
5181     case WEAPON_CLASS:
5182         alev = 10;
5183         break; /* artifact */
5184     case SCROLL_CLASS:
5185         alev = 9;
5186         break;
5187     case POTION_CLASS:
5188         alev = 6;
5189         break;
5190     case RING_CLASS:
5191         alev = 5;
5192         break;
5193     default:
5194         alev = u.ulevel;
5195         break; /* spell */
5196     }
5197     /* defense level */
5198     dlev = (int) mtmp->m_lev;
5199     if (dlev > 50)
5200         dlev = 50;
5201     else if (dlev < 1)
5202         dlev = is_mplayer(mtmp->data) ? u.ulevel : 1;
5203 
5204     resisted = rn2(100 + alev - dlev) < mtmp->data->mr;
5205     if (resisted) {
5206         if (tell) {
5207             shieldeff(mtmp->mx, mtmp->my);
5208             pline("%s resists!", Monnam(mtmp));
5209         }
5210         damage = (damage + 1) / 2;
5211     }
5212 
5213     if (damage) {
5214         mtmp->mhp -= damage;
5215         if (DEADMONSTER(mtmp)) {
5216             if (m_using)
5217                 monkilled(mtmp, "", AD_RBRE);
5218             else
5219                 killed(mtmp);
5220         }
5221     }
5222     return resisted;
5223 }
5224 
5225 #define MAXWISHTRY 5
5226 
5227 STATIC_OVL void
wishcmdassist(triesleft)5228 wishcmdassist(triesleft)
5229 int triesleft;
5230 {
5231     static NEARDATA const char *
5232         wishinfo[] = {
5233   "Wish details:",
5234   "",
5235   "Enter the name of an object, such as \"potion of monster detection\",",
5236   "\"scroll labeled README\", \"elven mithril-coat\", or \"Grimtooth\"",
5237   "(without the quotes).",
5238   "",
5239   "For object types which come in stacks, you may specify a plural name",
5240   "such as \"potions of healing\", or specify a count, such as \"1000 gold",
5241   "pieces\", although that aspect of your wish might not be granted.",
5242   "",
5243   "You may also specify various prefix values which might be used to",
5244   "modify the item, such as \"uncursed\" or \"rustproof\" or \"+1\".",
5245   "Most modifiers shown when viewing your inventory can be specified.",
5246   "",
5247   "You may specify 'nothing' to explicitly decline this wish.",
5248   0,
5249     },
5250         preserve_wishless[] = "Doing so will preserve 'wishless' conduct.",
5251         retry_info[] =
5252                     "If you specify an unrecognized object name %s%s time%s,",
5253         retry_too[] = "a randomly chosen item will be granted.",
5254         suppress_cmdassist[] =
5255             "(Suppress this assistance with !cmdassist in your config file.)",
5256         *cardinals[] = { "zero",  "one",  "two", "three", "four", "five" },
5257         too_many[] = "too many";
5258     int i;
5259     winid win;
5260     char buf[BUFSZ];
5261 
5262     win = create_nhwindow(NHW_TEXT);
5263     if (!win)
5264         return;
5265     for (i = 0; i < SIZE(wishinfo) - 1; ++i)
5266         putstr(win, 0, wishinfo[i]);
5267     if (!u.uconduct.wishes)
5268         putstr(win, 0, preserve_wishless);
5269     putstr(win, 0, "");
5270     Sprintf(buf, retry_info,
5271             (triesleft >= 0 && triesleft < SIZE(cardinals))
5272                ? cardinals[triesleft]
5273                : too_many,
5274             (triesleft < MAXWISHTRY) ? " more" : "",
5275             plur(triesleft));
5276     putstr(win, 0, buf);
5277     putstr(win, 0, retry_too);
5278     putstr(win, 0, "");
5279     if (iflags.cmdassist)
5280         putstr(win, 0, suppress_cmdassist);
5281     display_nhwindow(win, FALSE);
5282     destroy_nhwindow(win);
5283 }
5284 
5285 void
makewish()5286 makewish()
5287 {
5288     char buf[BUFSZ] = DUMMY;
5289     char promptbuf[BUFSZ];
5290     struct obj *otmp, nothing;
5291     int tries = 0;
5292 
5293     promptbuf[0] = '\0';
5294     nothing = zeroobj; /* lint suppression; only its address matters */
5295     if (flags.verbose)
5296         You("may wish for an object.");
5297  retry:
5298     Strcpy(promptbuf, "For what do you wish");
5299     if (iflags.cmdassist && tries > 0)
5300         Strcat(promptbuf, " (enter 'help' for assistance)");
5301     Strcat(promptbuf, "?");
5302     getlin(promptbuf, buf);
5303     (void) mungspaces(buf);
5304     if (buf[0] == '\033') {
5305         buf[0] = '\0';
5306     } else if (!strcmpi(buf, "help")) {
5307         wishcmdassist(MAXWISHTRY - tries);
5308         buf[0] = '\0'; /* for EDIT_GETLIN */
5309         goto retry;
5310     }
5311     /*
5312      *  Note: if they wished for and got a non-object successfully,
5313      *  otmp == &zeroobj.  That includes gold, or an artifact that
5314      *  has been denied.  Wishing for "nothing" requires a separate
5315      *  value to remain distinct.
5316      */
5317     otmp = readobjnam(buf, &nothing);
5318     if (!otmp) {
5319         pline("Nothing fitting that description exists in the game.");
5320         if (++tries < MAXWISHTRY)
5321             goto retry;
5322         pline1(thats_enough_tries);
5323         otmp = readobjnam((char *) 0, (struct obj *) 0);
5324         if (!otmp)
5325             return; /* for safety; should never happen */
5326     } else if (otmp == &nothing) {
5327         /* explicitly wished for "nothing", presumably attempting
5328            to retain wishless conduct */
5329         return;
5330     }
5331 
5332     /* KMH, conduct */
5333     u.uconduct.wishes++;
5334 
5335     if (otmp != &zeroobj) {
5336         const char
5337             *verb = ((Is_airlevel(&u.uz) || u.uinwater) ? "slip" : "drop"),
5338             *oops_msg = (u.uswallow
5339                          ? "Oops!  %s out of your reach!"
5340                          : (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
5341                             || levl[u.ux][u.uy].typ < IRONBARS
5342                             || levl[u.ux][u.uy].typ >= ICE)
5343                             ? "Oops!  %s away from you!"
5344                             : "Oops!  %s to the floor!");
5345 
5346         /* The(aobjnam()) is safe since otmp is unidentified -dlc */
5347         (void) hold_another_object(otmp, oops_msg,
5348                                    The(aobjnam(otmp, verb)),
5349                                    (const char *) 0);
5350         u.ublesscnt += rn1(100, 50); /* the gods take notice */
5351     }
5352 }
5353 
5354 /*zap.c*/
5355