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, ¬hing);
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 == ¬hing) {
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