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