dumplog message history groundwork
[aNetHack.git] / src / zap.c
blob9cf38e0302d9c96e223a483ac5e6498bbf4894a1
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, TRUE);
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 || (mons[montype].mlet == S_TROLL
753 && uwep && uwep->oartifact == ART_TROLLSBANE)) {
754 if (by_hero && cansee(x,y))
755 pline("%s twitches feebly.",
756 upstart(corpse_xname(corpse, (const char *) 0, CXN_PFX_THE)));
757 return (struct monst *) 0;
760 if (cant_revive(&montype, TRUE, corpse)) {
761 /* make a zombie or doppelganger instead */
762 /* note: montype has changed; mptr keeps old value for newcham() */
763 mtmp = makemon(&mons[montype], x, y, NO_MINVENT | MM_NOWAIT);
764 if (mtmp) {
765 /* skip ghost handling */
766 if (has_omid(corpse))
767 free_omid(corpse);
768 if (has_omonst(corpse))
769 free_omonst(corpse);
770 if (mtmp->cham == PM_DOPPELGANGER) {
771 /* change shape to match the corpse */
772 (void) newcham(mtmp, mptr, FALSE, FALSE);
773 } else if (mtmp->data->mlet == S_ZOMBIE) {
774 mtmp->mhp = mtmp->mhpmax = 100;
775 mon_adjust_speed(mtmp, 2, (struct obj *) 0); /* MFAST */
778 } else if (has_omonst(corpse)) {
779 /* use saved traits */
780 xy.x = x, xy.y = y;
781 mtmp = montraits(corpse, &xy);
782 if (mtmp && mtmp->mtame && !mtmp->isminion)
783 wary_dog(mtmp, TRUE);
784 } else {
785 /* make a new monster */
786 mtmp = makemon(mptr, x, y, NO_MINVENT | MM_NOWAIT | MM_NOCOUNTBIRTH);
788 if (!mtmp)
789 return (struct monst *) 0;
791 /* hiders shouldn't already be re-hidden when they revive */
792 if (mtmp->mundetected) {
793 mtmp->mundetected = 0;
794 newsym(mtmp->mx, mtmp->my);
796 if (mtmp->m_ap_type)
797 seemimic(mtmp);
799 one_of = (corpse->quan > 1L);
800 if (one_of)
801 corpse = splitobj(corpse, 1L);
803 /* if this is caused by the hero there might be a shop charge */
804 if (by_hero) {
805 struct monst *shkp = 0;
807 x = corpse->ox, y = corpse->oy;
808 if (costly_spot(x, y)
809 && (carried(corpse) ? corpse->unpaid : !corpse->no_charge))
810 shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
812 if (cansee(x, y)) {
813 char buf[BUFSZ];
814 unsigned pfx = CXN_PFX_THE;
816 Strcpy(buf, one_of ? "one of " : "");
817 if (carried(corpse) && !corpse->unpaid) {
818 Strcat(buf, "your ");
819 pfx = CXN_NO_PFX;
821 if (one_of)
822 corpse->quan++; /* force plural */
823 Strcat(buf, corpse_xname(corpse, (const char *) 0, pfx));
824 if (one_of) /* could be simplified to ''corpse->quan = 1L;'' */
825 corpse->quan--;
826 pline("%s glows iridescently.", upstart(buf));
827 } else if (shkp) {
828 /* need some prior description of the corpse since
829 stolen_value() will refer to the object as "it" */
830 pline("A corpse is resuscitated.");
832 /* don't charge for shopkeeper's own corpse if we just revived him */
833 if (shkp && mtmp != shkp)
834 (void) stolen_value(corpse, x, y, (boolean) shkp->mpeaceful,
835 FALSE);
837 /* [we don't give any comparable message about the corpse for
838 the !by_hero case because caller might have already done so] */
841 /* handle recorporealization of an active ghost */
842 if (has_omid(corpse)) {
843 unsigned m_id;
844 struct monst *ghost;
845 struct obj *otmp;
847 (void) memcpy((genericptr_t) &m_id, (genericptr_t) OMID(corpse),
848 sizeof m_id);
849 ghost = find_mid(m_id, FM_FMON);
850 if (ghost && ghost->data == &mons[PM_GHOST]) {
851 if (canseemon(ghost))
852 pline("%s is suddenly drawn into its former body!",
853 Monnam(ghost));
854 /* transfer the ghost's inventory along with it */
855 while ((otmp = ghost->minvent) != 0) {
856 obj_extract_self(otmp);
857 add_to_minv(mtmp, otmp);
859 /* tame the revived monster if its ghost was tame */
860 if (ghost->mtame && !mtmp->mtame) {
861 if (tamedog(mtmp, (struct obj *) 0)) {
862 /* ghost's edog data is ignored */
863 mtmp->mtame = ghost->mtame;
866 /* was ghost, now alive, it's all very confusing */
867 mtmp->mconf = 1;
868 /* separate ghost monster no longer exists */
869 mongone(ghost);
871 free_omid(corpse);
874 /* monster retains its name */
875 if (has_oname(corpse) && !unique_corpstat(mtmp->data))
876 mtmp = christen_monst(mtmp, ONAME(corpse));
877 /* partially eaten corpse yields wounded monster */
878 if (corpse->oeaten)
879 mtmp->mhp = eaten_stat(mtmp->mhp, corpse);
880 /* track that this monster was revived at least once */
881 mtmp->mrevived = 1;
883 /* finally, get rid of the corpse--it's gone now */
884 switch (corpse->where) {
885 case OBJ_INVENT:
886 useup(corpse);
887 break;
888 case OBJ_FLOOR:
889 /* in case MON_AT+enexto for invisible mon */
890 x = corpse->ox, y = corpse->oy;
891 /* not useupf(), which charges */
892 if (corpse->quan > 1L)
893 corpse = splitobj(corpse, 1L);
894 delobj(corpse);
895 newsym(x, y);
896 break;
897 case OBJ_MINVENT:
898 m_useup(corpse->ocarry, corpse);
899 break;
900 case OBJ_CONTAINED:
901 obj_extract_self(corpse);
902 obfree(corpse, (struct obj *) 0);
903 break;
904 default:
905 panic("revive");
908 return mtmp;
911 STATIC_OVL void
912 revive_egg(obj)
913 struct obj *obj;
916 * Note: generic eggs with corpsenm set to NON_PM will never hatch.
918 if (obj->otyp != EGG)
919 return;
920 if (obj->corpsenm != NON_PM && !dead_species(obj->corpsenm, TRUE))
921 attach_egg_hatch_timeout(obj, 0L);
924 /* try to revive all corpses and eggs carried by `mon' */
926 unturn_dead(mon)
927 struct monst *mon;
929 struct obj *otmp, *otmp2;
930 struct monst *mtmp2;
931 char owner[BUFSZ], corpse[BUFSZ];
932 boolean youseeit;
933 int res = 0;
935 youseeit = (mon == &youmonst) ? TRUE : canseemon(mon);
936 otmp2 = (mon == &youmonst) ? invent : mon->minvent;
937 owner[0] = corpse[0] = '\0'; /* lint suppression */
939 while ((otmp = otmp2) != 0) {
940 otmp2 = otmp->nobj;
941 if (otmp->otyp == EGG)
942 revive_egg(otmp);
943 if (otmp->otyp != CORPSE)
944 continue;
945 /* save the name; the object is liable to go away */
946 if (youseeit) {
947 Strcpy(corpse,
948 corpse_xname(otmp, (const char *) 0, CXN_SINGULAR));
949 Shk_Your(owner, otmp); /* includes a trailing space */
952 /* for a stack, only one is revived */
953 if ((mtmp2 = revive(otmp, !context.mon_moving)) != 0) {
954 ++res;
955 if (youseeit)
956 pline("%s%s suddenly comes alive!", owner, corpse);
957 else if (canseemon(mtmp2))
958 pline("%s suddenly appears!", Amonnam(mtmp2));
961 return res;
964 /* cancel obj, possibly carried by you or a monster */
965 void
966 cancel_item(obj)
967 register struct obj *obj;
969 boolean u_ring = (obj == uleft || obj == uright);
970 int otyp = obj->otyp;
972 switch (otyp) {
973 case RIN_GAIN_STRENGTH:
974 if ((obj->owornmask & W_RING) && u_ring) {
975 ABON(A_STR) -= obj->spe;
976 context.botl = 1;
978 break;
979 case RIN_GAIN_CONSTITUTION:
980 if ((obj->owornmask & W_RING) && u_ring) {
981 ABON(A_CON) -= obj->spe;
982 context.botl = 1;
984 break;
985 case RIN_ADORNMENT:
986 if ((obj->owornmask & W_RING) && u_ring) {
987 ABON(A_CHA) -= obj->spe;
988 context.botl = 1;
990 break;
991 case RIN_INCREASE_ACCURACY:
992 if ((obj->owornmask & W_RING) && u_ring)
993 u.uhitinc -= obj->spe;
994 break;
995 case RIN_INCREASE_DAMAGE:
996 if ((obj->owornmask & W_RING) && u_ring)
997 u.udaminc -= obj->spe;
998 break;
999 case GAUNTLETS_OF_DEXTERITY:
1000 if ((obj->owornmask & W_ARMG) && (obj == uarmg)) {
1001 ABON(A_DEX) -= obj->spe;
1002 context.botl = 1;
1004 break;
1005 case HELM_OF_BRILLIANCE:
1006 if ((obj->owornmask & W_ARMH) && (obj == uarmh)) {
1007 ABON(A_INT) -= obj->spe;
1008 ABON(A_WIS) -= obj->spe;
1009 context.botl = 1;
1011 break;
1012 /* case RIN_PROTECTION: not needed */
1014 if (objects[otyp].oc_magic
1015 || (obj->spe && (obj->oclass == ARMOR_CLASS
1016 || obj->oclass == WEAPON_CLASS || is_weptool(obj)))
1017 || otyp == POT_ACID
1018 || otyp == POT_SICKNESS
1019 || (otyp == POT_WATER && (obj->blessed || obj->cursed))) {
1020 if (obj->spe != ((obj->oclass == WAND_CLASS) ? -1 : 0)
1021 && otyp != WAN_CANCELLATION /* can't cancel cancellation */
1022 && otyp != MAGIC_LAMP /* cancelling doesn't remove djinni */
1023 && otyp != CANDELABRUM_OF_INVOCATION) {
1024 costly_alteration(obj, COST_CANCEL);
1025 obj->spe = (obj->oclass == WAND_CLASS) ? -1 : 0;
1027 switch (obj->oclass) {
1028 case SCROLL_CLASS:
1029 costly_alteration(obj, COST_CANCEL);
1030 obj->otyp = SCR_BLANK_PAPER;
1031 obj->spe = 0;
1032 break;
1033 case SPBOOK_CLASS:
1034 if (otyp != SPE_CANCELLATION && otyp != SPE_NOVEL
1035 && otyp != SPE_BOOK_OF_THE_DEAD) {
1036 costly_alteration(obj, COST_CANCEL);
1037 obj->otyp = SPE_BLANK_PAPER;
1039 break;
1040 case POTION_CLASS:
1041 costly_alteration(obj,
1042 (otyp != POT_WATER)
1043 ? COST_CANCEL
1044 : obj->cursed ? COST_UNCURS : COST_UNBLSS);
1045 if (otyp == POT_SICKNESS || otyp == POT_SEE_INVISIBLE) {
1046 /* sickness is "biologically contaminated" fruit juice;
1047 cancel it and it just becomes fruit juice...
1048 whereas see invisible tastes like "enchanted" fruit
1049 juice, it similarly cancels */
1050 obj->otyp = POT_FRUIT_JUICE;
1051 } else {
1052 obj->otyp = POT_WATER;
1053 obj->odiluted = 0; /* same as any other water */
1055 break;
1058 unbless(obj);
1059 uncurse(obj);
1060 return;
1063 /* Remove a positive enchantment or charge from obj,
1064 * possibly carried by you or a monster
1066 boolean
1067 drain_item(obj, by_you)
1068 struct obj *obj;
1069 boolean by_you;
1071 boolean u_ring;
1073 /* Is this a charged/enchanted object? */
1074 if (!obj
1075 || (!objects[obj->otyp].oc_charged && obj->oclass != WEAPON_CLASS
1076 && obj->oclass != ARMOR_CLASS && !is_weptool(obj))
1077 || obj->spe <= 0)
1078 return FALSE;
1079 if (defends(AD_DRLI, obj) || defends_when_carried(AD_DRLI, obj)
1080 || obj_resists(obj, 10, 90))
1081 return FALSE;
1083 /* Charge for the cost of the object */
1084 if (by_you)
1085 costly_alteration(obj, COST_DRAIN);
1087 /* Drain the object and any implied effects */
1088 obj->spe--;
1089 u_ring = (obj == uleft) || (obj == uright);
1090 switch (obj->otyp) {
1091 case RIN_GAIN_STRENGTH:
1092 if ((obj->owornmask & W_RING) && u_ring) {
1093 ABON(A_STR)--;
1094 context.botl = 1;
1096 break;
1097 case RIN_GAIN_CONSTITUTION:
1098 if ((obj->owornmask & W_RING) && u_ring) {
1099 ABON(A_CON)--;
1100 context.botl = 1;
1102 break;
1103 case RIN_ADORNMENT:
1104 if ((obj->owornmask & W_RING) && u_ring) {
1105 ABON(A_CHA)--;
1106 context.botl = 1;
1108 break;
1109 case RIN_INCREASE_ACCURACY:
1110 if ((obj->owornmask & W_RING) && u_ring)
1111 u.uhitinc--;
1112 break;
1113 case RIN_INCREASE_DAMAGE:
1114 if ((obj->owornmask & W_RING) && u_ring)
1115 u.udaminc--;
1116 break;
1117 case RIN_PROTECTION:
1118 if (u_ring)
1119 context.botl = 1; /* bot() will recalc u.uac */
1120 break;
1121 case HELM_OF_BRILLIANCE:
1122 if ((obj->owornmask & W_ARMH) && (obj == uarmh)) {
1123 ABON(A_INT)--;
1124 ABON(A_WIS)--;
1125 context.botl = 1;
1127 break;
1128 case GAUNTLETS_OF_DEXTERITY:
1129 if ((obj->owornmask & W_ARMG) && (obj == uarmg)) {
1130 ABON(A_DEX)--;
1131 context.botl = 1;
1133 break;
1134 default:
1135 break;
1137 if (context.botl)
1138 bot();
1139 if (carried(obj))
1140 update_inventory();
1141 return TRUE;
1144 boolean
1145 obj_resists(obj, ochance, achance)
1146 struct obj *obj;
1147 int ochance, achance; /* percent chance for ordinary objects, artifacts */
1149 if (obj->otyp == AMULET_OF_YENDOR
1150 || obj->otyp == SPE_BOOK_OF_THE_DEAD
1151 || obj->otyp == CANDELABRUM_OF_INVOCATION
1152 || obj->otyp == BELL_OF_OPENING
1153 || (obj->otyp == CORPSE && is_rider(&mons[obj->corpsenm]))) {
1154 return TRUE;
1155 } else {
1156 int chance = rn2(100);
1158 return (boolean) (chance < (obj->oartifact ? achance : ochance));
1162 boolean
1163 obj_shudders(obj)
1164 struct obj *obj;
1166 int zap_odds;
1168 if (context.bypasses && obj->bypass)
1169 return FALSE;
1171 if (obj->oclass == WAND_CLASS)
1172 zap_odds = 3; /* half-life = 2 zaps */
1173 else if (obj->cursed)
1174 zap_odds = 3; /* half-life = 2 zaps */
1175 else if (obj->blessed)
1176 zap_odds = 12; /* half-life = 8 zaps */
1177 else
1178 zap_odds = 8; /* half-life = 6 zaps */
1180 /* adjust for "large" quantities of identical things */
1181 if (obj->quan > 4L)
1182 zap_odds /= 2;
1184 return (boolean) !rn2(zap_odds);
1187 /* Use up at least minwt number of things made of material mat.
1188 * There's also a chance that other stuff will be used up. Finally,
1189 * there's a random factor here to keep from always using the stuff
1190 * at the top of the pile.
1192 STATIC_OVL void
1193 polyuse(objhdr, mat, minwt)
1194 struct obj *objhdr;
1195 int mat, minwt;
1197 register struct obj *otmp, *otmp2;
1199 for (otmp = objhdr; minwt > 0 && otmp; otmp = otmp2) {
1200 otmp2 = otmp->nexthere;
1201 if (context.bypasses && otmp->bypass)
1202 continue;
1203 if (otmp == uball || otmp == uchain)
1204 continue;
1205 if (obj_resists(otmp, 0, 0))
1206 continue; /* preserve unique objects */
1207 #ifdef MAIL
1208 if (otmp->otyp == SCR_MAIL)
1209 continue;
1210 #endif
1212 if (((int) objects[otmp->otyp].oc_material == mat)
1213 == (rn2(minwt + 1) != 0)) {
1214 /* appropriately add damage to bill */
1215 if (costly_spot(otmp->ox, otmp->oy)) {
1216 if (*u.ushops)
1217 addtobill(otmp, FALSE, FALSE, FALSE);
1218 else
1219 (void) stolen_value(otmp, otmp->ox, otmp->oy, FALSE,
1220 FALSE);
1222 if (otmp->quan < LARGEST_INT)
1223 minwt -= (int) otmp->quan;
1224 else
1225 minwt = 0;
1226 delobj(otmp);
1232 * Polymorph some of the stuff in this pile into a monster, preferably
1233 * a golem of the kind okind.
1235 STATIC_OVL void
1236 create_polymon(obj, okind)
1237 struct obj *obj;
1238 int okind;
1240 struct permonst *mdat = (struct permonst *) 0;
1241 struct monst *mtmp;
1242 const char *material;
1243 int pm_index;
1245 if (context.bypasses) {
1246 /* this is approximate because the "no golems" !obj->nexthere
1247 check below doesn't understand bypassed objects; but it
1248 should suffice since bypassed objects always end up as a
1249 consecutive group at the top of their pile */
1250 while (obj && obj->bypass)
1251 obj = obj->nexthere;
1254 /* no golems if you zap only one object -- not enough stuff */
1255 if (!obj || (!obj->nexthere && obj->quan == 1L))
1256 return;
1258 /* some of these choices are arbitrary */
1259 switch (okind) {
1260 case IRON:
1261 case METAL:
1262 case MITHRIL:
1263 pm_index = PM_IRON_GOLEM;
1264 material = "metal ";
1265 break;
1266 case COPPER:
1267 case SILVER:
1268 case PLATINUM:
1269 case GEMSTONE:
1270 case MINERAL:
1271 pm_index = rn2(2) ? PM_STONE_GOLEM : PM_CLAY_GOLEM;
1272 material = "lithic ";
1273 break;
1274 case 0:
1275 case FLESH:
1276 /* there is no flesh type, but all food is type 0, so we use it */
1277 pm_index = PM_FLESH_GOLEM;
1278 material = "organic ";
1279 break;
1280 case WOOD:
1281 pm_index = PM_WOOD_GOLEM;
1282 material = "wood ";
1283 break;
1284 case LEATHER:
1285 pm_index = PM_LEATHER_GOLEM;
1286 material = "leather ";
1287 break;
1288 case CLOTH:
1289 pm_index = PM_ROPE_GOLEM;
1290 material = "cloth ";
1291 break;
1292 case BONE:
1293 pm_index = PM_SKELETON; /* nearest thing to "bone golem" */
1294 material = "bony ";
1295 break;
1296 case GOLD:
1297 pm_index = PM_GOLD_GOLEM;
1298 material = "gold ";
1299 break;
1300 case GLASS:
1301 pm_index = PM_GLASS_GOLEM;
1302 material = "glassy ";
1303 break;
1304 case PAPER:
1305 pm_index = PM_PAPER_GOLEM;
1306 material = "paper ";
1307 break;
1308 default:
1309 /* if all else fails... */
1310 pm_index = PM_STRAW_GOLEM;
1311 material = "";
1312 break;
1315 if (!(mvitals[pm_index].mvflags & G_GENOD))
1316 mdat = &mons[pm_index];
1318 mtmp = makemon(mdat, obj->ox, obj->oy, NO_MM_FLAGS);
1319 polyuse(obj, okind, (int) mons[pm_index].cwt);
1321 if (mtmp && cansee(mtmp->mx, mtmp->my)) {
1322 pline("Some %sobjects meld, and %s arises from the pile!", material,
1323 a_monnam(mtmp));
1327 /* Assumes obj is on the floor. */
1328 void
1329 do_osshock(obj)
1330 struct obj *obj;
1332 long i;
1334 #ifdef MAIL
1335 if (obj->otyp == SCR_MAIL)
1336 return;
1337 #endif
1338 obj_zapped = TRUE;
1340 if (poly_zapped < 0) {
1341 /* some may metamorphosize */
1342 for (i = obj->quan; i; i--)
1343 if (!rn2(Luck + 45)) {
1344 poly_zapped = objects[obj->otyp].oc_material;
1345 break;
1349 /* if quan > 1 then some will survive intact */
1350 if (obj->quan > 1L) {
1351 if (obj->quan > LARGEST_INT)
1352 obj = splitobj(obj, (long) rnd(30000));
1353 else
1354 obj = splitobj(obj, (long) rnd((int) obj->quan - 1));
1357 /* appropriately add damage to bill */
1358 if (costly_spot(obj->ox, obj->oy)) {
1359 if (*u.ushops)
1360 addtobill(obj, FALSE, FALSE, FALSE);
1361 else
1362 (void) stolen_value(obj, obj->ox, obj->oy, FALSE, FALSE);
1365 /* zap the object */
1366 delobj(obj);
1369 /* classes of items whose current charge count carries over across polymorph
1371 static const char charged_objs[] = { WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS,
1372 '\0' };
1375 * Polymorph the object to the given object ID. If the ID is STRANGE_OBJECT
1376 * then pick random object from the source's class (this is the standard
1377 * "polymorph" case). If ID is set to a specific object, inhibit fusing
1378 * n objects into 1. This could have been added as a flag, but currently
1379 * it is tied to not being the standard polymorph case. The new polymorphed
1380 * object replaces obj in its link chains. Return value is a pointer to
1381 * the new object.
1383 * This should be safe to call for an object anywhere.
1385 struct obj *
1386 poly_obj(obj, id)
1387 struct obj *obj;
1388 int id;
1390 struct obj *otmp;
1391 xchar ox, oy;
1392 boolean can_merge = (id == STRANGE_OBJECT);
1393 int obj_location = obj->where;
1395 if (obj->otyp == BOULDER)
1396 sokoban_guilt();
1397 if (id == STRANGE_OBJECT) { /* preserve symbol */
1398 int try_limit = 3;
1399 unsigned magic_obj = objects[obj->otyp].oc_magic;
1401 if (obj->otyp == UNICORN_HORN && obj->degraded_horn)
1402 magic_obj = 0;
1403 /* Try up to 3 times to make the magic-or-not status of
1404 the new item be the same as it was for the old one. */
1405 otmp = (struct obj *) 0;
1406 do {
1407 if (otmp)
1408 delobj(otmp);
1409 otmp = mkobj(obj->oclass, FALSE);
1410 } while (--try_limit > 0
1411 && objects[otmp->otyp].oc_magic != magic_obj);
1412 } else {
1413 /* literally replace obj with this new thing */
1414 otmp = mksobj(id, FALSE, FALSE);
1415 /* Actually more things use corpsenm but they polymorph differently */
1416 #define USES_CORPSENM(typ) \
1417 ((typ) == CORPSE || (typ) == STATUE || (typ) == FIGURINE)
1419 if (USES_CORPSENM(obj->otyp) && USES_CORPSENM(id))
1420 set_corpsenm(otmp, obj->corpsenm);
1421 #undef USES_CORPSENM
1424 /* preserve quantity */
1425 otmp->quan = obj->quan;
1426 /* preserve the shopkeepers (lack of) interest */
1427 otmp->no_charge = obj->no_charge;
1428 /* preserve inventory letter if in inventory */
1429 if (obj_location == OBJ_INVENT)
1430 otmp->invlet = obj->invlet;
1431 #ifdef MAIL
1432 /* You can't send yourself 100 mail messages and then
1433 * polymorph them into useful scrolls
1435 if (obj->otyp == SCR_MAIL) {
1436 otmp->otyp = SCR_MAIL;
1437 otmp->spe = 1;
1439 #endif
1441 /* avoid abusing eggs laid by you */
1442 if (obj->otyp == EGG && obj->spe) {
1443 int mnum, tryct = 100;
1445 /* first, turn into a generic egg */
1446 if (otmp->otyp == EGG)
1447 kill_egg(otmp);
1448 else {
1449 otmp->otyp = EGG;
1450 otmp->owt = weight(otmp);
1452 otmp->corpsenm = NON_PM;
1453 otmp->spe = 0;
1455 /* now change it into something laid by the hero */
1456 while (tryct--) {
1457 mnum = can_be_hatched(random_monster());
1458 if (mnum != NON_PM && !dead_species(mnum, TRUE)) {
1459 otmp->spe = 1; /* laid by hero */
1460 set_corpsenm(otmp, mnum); /* also sets hatch timer */
1461 break;
1466 /* keep special fields (including charges on wands) */
1467 if (index(charged_objs, otmp->oclass))
1468 otmp->spe = obj->spe;
1469 otmp->recharged = obj->recharged;
1471 otmp->cursed = obj->cursed;
1472 otmp->blessed = obj->blessed;
1474 if (erosion_matters(otmp)) {
1475 if (is_flammable(otmp) || is_rustprone(otmp))
1476 otmp->oeroded = obj->oeroded;
1477 if (is_corrodeable(otmp) || is_rottable(otmp))
1478 otmp->oeroded2 = obj->oeroded2;
1479 if (is_damageable(otmp))
1480 otmp->oerodeproof = obj->oerodeproof;
1483 /* Keep chest/box traps and poisoned ammo if we may */
1484 if (obj->otrapped && Is_box(otmp))
1485 otmp->otrapped = TRUE;
1487 if (obj->opoisoned && is_poisonable(otmp))
1488 otmp->opoisoned = TRUE;
1490 if (id == STRANGE_OBJECT && obj->otyp == CORPSE) {
1491 /* turn crocodile corpses into shoes */
1492 if (obj->corpsenm == PM_CROCODILE) {
1493 otmp->otyp = LOW_BOOTS;
1494 otmp->oclass = ARMOR_CLASS;
1495 otmp->spe = 0;
1496 otmp->oeroded = 0;
1497 otmp->oerodeproof = TRUE;
1498 otmp->quan = 1L;
1499 otmp->cursed = FALSE;
1503 /* no box contents --KAA */
1504 if (Has_contents(otmp))
1505 delete_contents(otmp);
1507 /* 'n' merged objects may be fused into 1 object */
1508 if (otmp->quan > 1L && (!objects[otmp->otyp].oc_merge
1509 || (can_merge && otmp->quan > (long) rn2(1000))))
1510 otmp->quan = 1L;
1512 switch (otmp->oclass) {
1513 case TOOL_CLASS:
1514 if (otmp->otyp == MAGIC_LAMP) {
1515 otmp->otyp = OIL_LAMP;
1516 otmp->age = 1500L; /* "best" oil lamp possible */
1517 } else if (otmp->otyp == MAGIC_MARKER) {
1518 otmp->recharged = 1; /* degraded quality */
1520 /* don't care about the recharge count of other tools */
1521 break;
1523 case WAND_CLASS:
1524 while (otmp->otyp == WAN_WISHING || otmp->otyp == WAN_POLYMORPH)
1525 otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING);
1526 /* altering the object tends to degrade its quality
1527 (analogous to spellbook `read count' handling) */
1528 if ((int) otmp->recharged < rn2(7)) /* recharge_limit */
1529 otmp->recharged++;
1530 break;
1532 case POTION_CLASS:
1533 while (otmp->otyp == POT_POLYMORPH)
1534 otmp->otyp = rnd_class(POT_GAIN_ABILITY, POT_WATER);
1535 break;
1537 case SPBOOK_CLASS:
1538 while (otmp->otyp == SPE_POLYMORPH)
1539 otmp->otyp = rnd_class(SPE_DIG, SPE_BLANK_PAPER);
1540 /* reduce spellbook abuse; non-blank books degrade */
1541 if (otmp->otyp != SPE_BLANK_PAPER) {
1542 otmp->spestudied = obj->spestudied + 1;
1543 if (otmp->spestudied > MAX_SPELL_STUDY) {
1544 otmp->otyp = SPE_BLANK_PAPER;
1545 /* writing a new book over it will yield an unstudied
1546 one; re-polymorphing this one as-is may or may not
1547 get something non-blank */
1548 otmp->spestudied = rn2(otmp->spestudied);
1551 break;
1553 case GEM_CLASS:
1554 if (otmp->quan > (long) rnd(4)
1555 && objects[obj->otyp].oc_material == MINERAL
1556 && objects[otmp->otyp].oc_material != MINERAL) {
1557 otmp->otyp = ROCK; /* transmutation backfired */
1558 otmp->quan /= 2L; /* some material has been lost */
1560 break;
1563 /* update the weight */
1564 otmp->owt = weight(otmp);
1566 /* handle polymorph of worn item: stone-to-flesh cast on self can
1567 affect multiple objects at once, but their new forms won't
1568 produce any side-effects; a single worn item dipped into potion
1569 of polymorph can produce side-effects but those won't yield out
1570 of sequence messages because current polymorph is finished */
1571 if (obj_location == OBJ_INVENT && obj->owornmask) {
1572 long old_wornmask = obj->owornmask & ~(W_ART | W_ARTI),
1573 new_wornmask = wearslot(otmp);
1574 boolean was_twohanded = bimanual(obj), was_twoweap = u.twoweap;
1576 remove_worn_item(obj, TRUE);
1577 /* if the new form can be worn in the same slot, make it so
1578 [possible extension: if it could be worn in some other
1579 slot which is currently unfilled, wear it there instead] */
1580 if ((old_wornmask & W_QUIVER) != 0L) {
1581 setuqwep(otmp);
1582 } else if ((old_wornmask & W_SWAPWEP) != 0L) {
1583 if (was_twohanded || !bimanual(otmp))
1584 setuswapwep(otmp);
1585 if (was_twoweap && uswapwep)
1586 u.twoweap = TRUE;
1587 } else if ((old_wornmask & W_WEP) != 0L) {
1588 if (was_twohanded || !bimanual(otmp) || !uarms)
1589 setuwep(otmp);
1590 if (was_twoweap && uwep && !bimanual(uwep))
1591 u.twoweap = TRUE;
1592 } else if ((old_wornmask & new_wornmask) != 0L) {
1593 new_wornmask &= old_wornmask;
1594 setworn(otmp, new_wornmask);
1595 set_wear(otmp); /* Armor_on() for side-effects */
1599 /* ** we are now done adjusting the object ** */
1601 /* swap otmp for obj */
1602 replace_object(obj, otmp);
1603 if (obj_location == OBJ_INVENT) {
1605 * We may need to do extra adjustments for the hero if we're
1606 * messing with the hero's inventory. The following calls are
1607 * equivalent to calling freeinv on obj and addinv on otmp,
1608 * while doing an in-place swap of the actual objects.
1610 freeinv_core(obj);
1611 addinv_core1(otmp);
1612 addinv_core2(otmp);
1613 } else if (obj_location == OBJ_FLOOR) {
1614 ox = otmp->ox, oy = otmp->oy; /* set by replace_object() */
1615 if (obj->otyp == BOULDER && otmp->otyp != BOULDER
1616 && !does_block(ox, oy, &levl[ox][oy]))
1617 unblock_point(ox, oy);
1618 else if (obj->otyp != BOULDER && otmp->otyp == BOULDER)
1619 /* (checking does_block() here would be redundant) */
1620 block_point(ox, oy);
1623 if ((!carried(otmp) || obj->unpaid)
1624 && get_obj_location(otmp, &ox, &oy, BURIED_TOO | CONTAINED_TOO)
1625 && costly_spot(ox, oy)) {
1626 register struct monst *shkp =
1627 shop_keeper(*in_rooms(ox, oy, SHOPBASE));
1629 if ((!obj->no_charge
1630 || (Has_contents(obj)
1631 && (contained_cost(obj, shkp, 0L, FALSE, FALSE) != 0L)))
1632 && inhishop(shkp)) {
1633 if (shkp->mpeaceful) {
1634 if (*u.ushops
1635 && *in_rooms(u.ux, u.uy, 0)
1636 == *in_rooms(shkp->mx, shkp->my, 0)
1637 && !costly_spot(u.ux, u.uy)) {
1638 make_angry_shk(shkp, ox, oy);
1639 } else {
1640 pline("%s gets angry!", Monnam(shkp));
1641 hot_pursuit(shkp);
1643 } else
1644 Norep("%s is furious!", Monnam(shkp));
1647 delobj(obj);
1648 return otmp;
1651 /* stone-to-flesh spell hits and maybe transforms or animates obj */
1652 STATIC_OVL int
1653 stone_to_flesh_obj(obj)
1654 struct obj *obj;
1656 int res = 1; /* affected object by default */
1657 struct permonst *ptr;
1658 struct monst *mon, *shkp;
1659 struct obj *item;
1660 xchar oox, ooy;
1661 boolean smell = FALSE, golem_xform = FALSE;
1663 if (objects[obj->otyp].oc_material != MINERAL
1664 && objects[obj->otyp].oc_material != GEMSTONE)
1665 return 0;
1666 /* Heart of Ahriman usually resists; ordinary items rarely do */
1667 if (obj_resists(obj, 2, 98))
1668 return 0;
1670 (void) get_obj_location(obj, &oox, &ooy, 0);
1671 /* add more if stone objects are added.. */
1672 switch (objects[obj->otyp].oc_class) {
1673 case ROCK_CLASS: /* boulders and statues */
1674 case TOOL_CLASS: /* figurines */
1675 if (obj->otyp == BOULDER) {
1676 obj = poly_obj(obj, HUGE_CHUNK_OF_MEAT);
1677 smell = TRUE;
1678 } else if (obj->otyp == STATUE || obj->otyp == FIGURINE) {
1679 ptr = &mons[obj->corpsenm];
1680 if (is_golem(ptr)) {
1681 golem_xform = (ptr != &mons[PM_FLESH_GOLEM]);
1682 } else if (vegetarian(ptr)) {
1683 /* Don't animate monsters that aren't flesh */
1684 obj = poly_obj(obj, MEATBALL);
1685 smell = TRUE;
1686 break;
1688 if (obj->otyp == STATUE) {
1689 /* animate_statue() forces all golems to become flesh golems */
1690 mon = animate_statue(obj, oox, ooy, ANIMATE_SPELL, (int *) 0);
1691 } else { /* (obj->otyp == FIGURINE) */
1692 if (golem_xform)
1693 ptr = &mons[PM_FLESH_GOLEM];
1694 mon = makemon(ptr, oox, ooy, NO_MINVENT);
1695 if (mon) {
1696 if (costly_spot(oox, ooy)
1697 && (carried(obj) ? obj->unpaid : !obj->no_charge)) {
1698 shkp = shop_keeper(*in_rooms(oox, ooy, SHOPBASE));
1699 stolen_value(obj, oox, ooy,
1700 (shkp && shkp->mpeaceful), FALSE);
1702 if (obj->timed)
1703 obj_stop_timers(obj);
1704 if (carried(obj))
1705 useup(obj);
1706 else
1707 delobj(obj);
1708 if (cansee(mon->mx, mon->my))
1709 pline_The("figurine %sanimates!",
1710 golem_xform ? "turns to flesh and " : "");
1713 if (mon) {
1714 ptr = mon->data;
1715 /* this golem handling is redundant... */
1716 if (is_golem(ptr) && ptr != &mons[PM_FLESH_GOLEM])
1717 (void) newcham(mon, &mons[PM_FLESH_GOLEM], TRUE, FALSE);
1718 } else if ((ptr->geno & (G_NOCORPSE | G_UNIQ)) != 0) {
1719 /* didn't revive but can't leave corpse either */
1720 res = 0;
1721 } else {
1722 /* unlikely to get here since genociding monsters also
1723 sets the G_NOCORPSE flag; drop statue's contents */
1724 while ((item = obj->cobj) != 0) {
1725 bypass_obj(item); /* make stone-to-flesh miss it */
1726 obj_extract_self(item);
1727 place_object(item, oox, ooy);
1729 obj = poly_obj(obj, CORPSE);
1731 } else { /* miscellaneous tool or unexpected rock... */
1732 res = 0;
1734 break;
1735 /* maybe add weird things to become? */
1736 case RING_CLASS: /* some of the rings are stone */
1737 obj = poly_obj(obj, MEAT_RING);
1738 smell = TRUE;
1739 break;
1740 case WAND_CLASS: /* marble wand */
1741 obj = poly_obj(obj, MEAT_STICK);
1742 smell = TRUE;
1743 break;
1744 case GEM_CLASS: /* stones & gems */
1745 obj = poly_obj(obj, MEATBALL);
1746 smell = TRUE;
1747 break;
1748 case WEAPON_CLASS: /* crysknife */
1749 /*FALLTHRU*/
1750 default:
1751 res = 0;
1752 break;
1755 if (smell) {
1756 /* non-meat eaters smell meat, meat eaters smell its flavor;
1757 monks are considered non-meat eaters regardless of behavior;
1758 other roles are non-meat eaters if they haven't broken
1759 vegetarian conduct yet (or if poly'd into non-carnivorous/
1760 non-omnivorous form, regardless of whether it's herbivorous,
1761 non-eating, or something stranger) */
1762 if (Role_if(PM_MONK) || !u.uconduct.unvegetarian
1763 || !carnivorous(youmonst.data))
1764 Norep("You smell the odor of meat.");
1765 else
1766 Norep("You smell a delicious smell.");
1768 newsym(oox, ooy);
1769 return res;
1773 * Object obj was hit by the effect of the wand/spell otmp. Return
1774 * non-zero if the wand/spell had any effect.
1777 bhito(obj, otmp)
1778 struct obj *obj, *otmp;
1780 int res = 1; /* affected object by default */
1781 boolean learn_it = FALSE, maybelearnit;
1783 /* fundamental: a wand effect hitting itself doesn't do anything;
1784 otherwise we need to guard against accessing otmp after something
1785 strange has happened to it (along the lines of polymorph or
1786 stone-to-flesh [which aren't good examples since polymorph wands
1787 aren't affected by polymorph zaps and stone-to-flesh isn't
1788 available in wand form, but the concept still applies...]) */
1789 if (obj == otmp)
1790 return 0;
1792 if (obj->bypass) {
1793 /* The bypass bit is currently only used as follows:
1795 * POLYMORPH - When a monster being polymorphed drops something
1796 * from its inventory as a result of the change.
1797 * If the items fall to the floor, they are not
1798 * subject to direct subsequent polymorphing
1799 * themselves on that same zap. This makes it
1800 * consistent with items that remain in the
1801 * monster's inventory. They are not polymorphed
1802 * either.
1803 * UNDEAD_TURNING - When an undead creature gets killed via
1804 * undead turning, prevent its corpse from being
1805 * immediately revived by the same effect.
1806 * STONE_TO_FLESH - If a statue can't be revived, its
1807 * contents get dropped before turning it into
1808 * meat; prevent those contents from being hit.
1809 * retouch_equipment() - bypass flag is used to track which
1810 * items have been handled (bhito isn't involved).
1811 * menu_drop(), askchain() - inventory traversal where multiple
1812 * Drop can alter the invent chain while traversal
1813 * is in progress (bhito isn't involved).
1815 * The bypass bit on all objects is reset each turn, whenever
1816 * context.bypasses is set.
1818 * We check the obj->bypass bit above AND context.bypasses
1819 * as a safeguard against any stray occurrence left in an obj
1820 * struct someplace, although that should never happen.
1822 if (context.bypasses) {
1823 return 0;
1824 } else {
1825 debugpline1("%s for a moment.", Tobjnam(obj, "pulsate"));
1826 obj->bypass = 0;
1831 * Some parts of this function expect the object to be on the floor
1832 * obj->{ox,oy} to be valid. The exception to this (so far) is
1833 * for the STONE_TO_FLESH spell.
1835 if (!(obj->where == OBJ_FLOOR || otmp->otyp == SPE_STONE_TO_FLESH))
1836 impossible("bhito: obj is not floor or Stone To Flesh spell");
1838 if (obj == uball) {
1839 res = 0;
1840 } else if (obj == uchain) {
1841 if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) {
1842 learn_it = TRUE;
1843 unpunish();
1844 } else
1845 res = 0;
1846 } else
1847 switch (otmp->otyp) {
1848 case WAN_POLYMORPH:
1849 case SPE_POLYMORPH:
1850 if (obj->otyp == WAN_POLYMORPH || obj->otyp == SPE_POLYMORPH
1851 || obj->otyp == POT_POLYMORPH || obj_resists(obj, 5, 95)) {
1852 res = 0;
1853 break;
1855 /* KMH, conduct */
1856 u.uconduct.polypiles++;
1857 /* any saved lock context will be dangerously obsolete */
1858 if (Is_box(obj))
1859 (void) boxlock(obj, otmp);
1861 if (obj_shudders(obj)) {
1862 boolean cover =
1863 ((obj == level.objects[u.ux][u.uy]) && u.uundetected
1864 && hides_under(youmonst.data));
1866 if (cansee(obj->ox, obj->oy))
1867 learn_it = TRUE;
1868 do_osshock(obj);
1869 /* eek - your cover might have been blown */
1870 if (cover)
1871 (void) hideunder(&youmonst);
1872 break;
1874 obj = poly_obj(obj, STRANGE_OBJECT);
1875 newsym(obj->ox, obj->oy);
1876 break;
1877 case WAN_PROBING:
1878 res = !obj->dknown;
1879 /* target object has now been "seen (up close)" */
1880 obj->dknown = 1;
1881 if (Is_container(obj) || obj->otyp == STATUE) {
1882 obj->cknown = obj->lknown = 1;
1883 if (!obj->cobj) {
1884 boolean catbox = SchroedingersBox(obj);
1886 /* we don't want to force alive vs dead
1887 determination for Schroedinger's Cat here,
1888 so just make probing be inconclusive for it */
1889 if (catbox)
1890 obj->cknown = 0;
1891 pline("%s empty.", Tobjnam(obj, catbox ? "seem" : "are"));
1892 } else {
1893 struct obj *o;
1894 /* view contents (not recursively) */
1895 for (o = obj->cobj; o; o = o->nobj)
1896 o->dknown = 1; /* "seen", even if blind */
1897 (void) display_cinventory(obj);
1899 res = 1;
1901 if (res)
1902 learn_it = TRUE;
1903 break;
1904 case WAN_STRIKING:
1905 case SPE_FORCE_BOLT:
1906 /* learn the type if you see or hear something break
1907 (the sound could be implicit) */
1908 maybelearnit = cansee(obj->ox, obj->oy) || !Deaf;
1909 if (obj->otyp == BOULDER) {
1910 if (cansee(obj->ox, obj->oy))
1911 pline_The("boulder falls apart.");
1912 else
1913 You_hear("a crumbling sound.");
1914 fracture_rock(obj);
1915 } else if (obj->otyp == STATUE) {
1916 if (break_statue(obj)) {
1917 if (cansee(obj->ox, obj->oy)) {
1918 if (Hallucination)
1919 pline_The("%s shatters.", rndmonnam(NULL));
1920 else
1921 pline_The("statue shatters.");
1922 } else
1923 You_hear("a crumbling sound.");
1925 } else {
1926 int oox = obj->ox;
1927 int ooy = obj->oy;
1928 if (context.mon_moving
1929 ? !breaks(obj, obj->ox, obj->oy)
1930 : !hero_breaks(obj, obj->ox, obj->oy, FALSE))
1931 maybelearnit = FALSE; /* nothing broke */
1932 else
1933 newsym_force(oox,ooy);
1934 res = 0;
1936 if (maybelearnit)
1937 learn_it = TRUE;
1938 break;
1939 case WAN_CANCELLATION:
1940 case SPE_CANCELLATION:
1941 cancel_item(obj);
1942 #ifdef TEXTCOLOR
1943 newsym(obj->ox, obj->oy); /* might change color */
1944 #endif
1945 break;
1946 case SPE_DRAIN_LIFE:
1947 (void) drain_item(obj, TRUE);
1948 break;
1949 case WAN_TELEPORTATION:
1950 case SPE_TELEPORT_AWAY:
1951 (void) rloco(obj);
1952 break;
1953 case WAN_MAKE_INVISIBLE:
1954 break;
1955 case WAN_UNDEAD_TURNING:
1956 case SPE_TURN_UNDEAD:
1957 if (obj->otyp == EGG) {
1958 revive_egg(obj);
1959 } else if (obj->otyp == CORPSE) {
1960 int corpsenm = corpse_revive_type(obj);
1962 res = !!revive(obj, TRUE);
1963 if (res && Role_if(PM_HEALER)) {
1964 if (Hallucination && !Deaf) {
1965 You_hear("the sound of a defibrillator.");
1966 learn_it = TRUE;
1967 } else if (!Blind) {
1968 You("observe %s %s change dramatically.",
1969 s_suffix(an(mons[corpsenm].mname)),
1970 nonliving(&mons[corpsenm]) ? "motility"
1971 : "health");
1972 learn_it = TRUE;
1974 if (learn_it)
1975 exercise(A_WIS, TRUE);
1978 break;
1979 case WAN_OPENING:
1980 case SPE_KNOCK:
1981 case WAN_LOCKING:
1982 case SPE_WIZARD_LOCK:
1983 if (Is_box(obj))
1984 res = boxlock(obj, otmp);
1985 else
1986 res = 0;
1987 if (res)
1988 learn_it = TRUE;
1989 break;
1990 case WAN_SLOW_MONSTER: /* no effect on objects */
1991 case SPE_SLOW_MONSTER:
1992 case WAN_SPEED_MONSTER:
1993 case WAN_NOTHING:
1994 case SPE_HEALING:
1995 case SPE_EXTRA_HEALING:
1996 res = 0;
1997 break;
1998 case SPE_STONE_TO_FLESH:
1999 res = stone_to_flesh_obj(obj);
2000 break;
2001 default:
2002 impossible("What an interesting effect (%d)", otmp->otyp);
2003 break;
2005 /* if effect was observable then discover the wand type provided
2006 that the wand itself has been seen */
2007 if (learn_it)
2008 learnwand(otmp);
2009 return res;
2012 /* returns nonzero if something was hit */
2014 bhitpile(obj, fhito, tx, ty, zz)
2015 struct obj *obj;
2016 int FDECL((*fhito), (OBJ_P, OBJ_P));
2017 int tx, ty;
2018 schar zz;
2020 int hitanything = 0;
2021 register struct obj *otmp, *next_obj;
2023 if (obj->otyp == SPE_FORCE_BOLT || obj->otyp == WAN_STRIKING) {
2024 struct trap *t = t_at(tx, ty);
2026 /* We can't settle for the default calling sequence of
2027 bhito(otmp) -> break_statue(otmp) -> activate_statue_trap(ox,oy)
2028 because that last call might end up operating on our `next_obj'
2029 (below), rather than on the current object, if it happens to
2030 encounter a statue which mustn't become animated. */
2031 if (t && t->ttyp == STATUE_TRAP
2032 && activate_statue_trap(t, tx, ty, TRUE))
2033 learnwand(obj);
2036 poly_zapped = -1;
2037 for (otmp = level.objects[tx][ty]; otmp; otmp = next_obj) {
2038 next_obj = otmp->nexthere;
2039 /* for zap downwards, don't hit object poly'd hero is hiding under */
2040 if (zz > 0 && u.uundetected && otmp == level.objects[u.ux][u.uy]
2041 && hides_under(youmonst.data))
2042 continue;
2044 hitanything += (*fhito)(otmp, obj);
2046 if (poly_zapped >= 0)
2047 create_polymon(level.objects[tx][ty], poly_zapped);
2049 return hitanything;
2053 * zappable - returns 1 if zap is available, 0 otherwise.
2054 * it removes a charge from the wand if zappable.
2055 * added by GAN 11/03/86
2058 zappable(wand)
2059 register struct obj *wand;
2061 if (wand->spe < 0 || (wand->spe == 0 && rn2(121)))
2062 return 0;
2063 if (wand->spe == 0)
2064 You("wrest one last charge from the worn-out wand.");
2065 wand->spe--;
2066 return 1;
2070 * zapnodir - zaps a NODIR wand/spell.
2071 * added by GAN 11/03/86
2073 void
2074 zapnodir(obj)
2075 register struct obj *obj;
2077 boolean known = FALSE;
2079 switch (obj->otyp) {
2080 case WAN_LIGHT:
2081 case SPE_LIGHT:
2082 litroom(TRUE, obj);
2083 if (!Blind)
2084 known = TRUE;
2085 if (lightdamage(obj, TRUE, 5))
2086 known = TRUE;
2087 break;
2088 case WAN_SECRET_DOOR_DETECTION:
2089 case SPE_DETECT_UNSEEN:
2090 if (!findit())
2091 return;
2092 if (!Blind)
2093 known = TRUE;
2094 break;
2095 case WAN_CREATE_MONSTER:
2096 known = create_critters(rn2(23) ? 1 : rn1(7, 2),
2097 (struct permonst *) 0, FALSE);
2098 break;
2099 case WAN_WISHING:
2100 known = TRUE;
2101 if (Luck + rn2(5) < 0) {
2102 pline("Unfortunately, nothing happens.");
2103 break;
2105 makewish();
2106 break;
2107 case WAN_ENLIGHTENMENT:
2108 known = TRUE;
2109 You_feel("self-knowledgeable...");
2110 display_nhwindow(WIN_MESSAGE, FALSE);
2111 enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS);
2112 pline_The("feeling subsides.");
2113 exercise(A_WIS, TRUE);
2114 break;
2116 if (known) {
2117 if (!objects[obj->otyp].oc_name_known)
2118 more_experienced(0, 10);
2119 /* effect was observable; discover the wand type provided
2120 that the wand itself has been seen */
2121 learnwand(obj);
2125 STATIC_OVL void
2126 backfire(otmp)
2127 struct obj *otmp;
2129 int dmg;
2130 otmp->in_use = TRUE; /* in case losehp() is fatal */
2131 pline("%s suddenly explodes!", The(xname(otmp)));
2132 dmg = d(otmp->spe + 2, 6);
2133 losehp(Maybe_Half_Phys(dmg), "exploding wand", KILLED_BY_AN);
2134 useup(otmp);
2137 static NEARDATA const char zap_syms[] = { WAND_CLASS, 0 };
2139 /* 'z' command (or 'y' if numbed_pad==-1) */
2141 dozap()
2143 register struct obj *obj;
2144 int damage;
2146 if (check_capacity((char *) 0))
2147 return 0;
2148 obj = getobj(zap_syms, "zap");
2149 if (!obj)
2150 return 0;
2152 check_unpaid(obj);
2154 /* zappable addition done by GAN 11/03/86 */
2155 if (!zappable(obj))
2156 pline1(nothing_happens);
2157 else if (obj->cursed && !rn2(WAND_BACKFIRE_CHANCE)) {
2158 backfire(obj); /* the wand blows up in your face! */
2159 exercise(A_STR, FALSE);
2160 return 1;
2161 } else if (!(objects[obj->otyp].oc_dir == NODIR) && !getdir((char *) 0)) {
2162 if (!Blind)
2163 pline("%s glows and fades.", The(xname(obj)));
2164 /* make him pay for knowing !NODIR */
2165 } else if (!u.dx && !u.dy && !u.dz
2166 && !(objects[obj->otyp].oc_dir == NODIR)) {
2167 if ((damage = zapyourself(obj, TRUE)) != 0) {
2168 char buf[BUFSZ];
2170 Sprintf(buf, "zapped %sself with a wand", uhim());
2171 losehp(Maybe_Half_Phys(damage), buf, NO_KILLER_PREFIX);
2173 } else {
2174 /* Are we having fun yet?
2175 * weffects -> buzz(obj->otyp) -> zhitm (temple priest) ->
2176 * attack -> hitum -> known_hitum -> ghod_hitsu ->
2177 * buzz(AD_ELEC) -> destroy_item(WAND_CLASS) ->
2178 * useup -> obfree -> dealloc_obj -> free(obj)
2180 current_wand = obj;
2181 weffects(obj);
2182 obj = current_wand;
2183 current_wand = 0;
2185 if (obj && obj->spe < 0) {
2186 pline("%s to dust.", Tobjnam(obj, "turn"));
2187 useup(obj);
2189 update_inventory(); /* maybe used a charge */
2190 return 1;
2194 zapyourself(obj, ordinary)
2195 struct obj *obj;
2196 boolean ordinary;
2198 boolean learn_it = FALSE;
2199 int damage = 0;
2201 switch (obj->otyp) {
2202 case WAN_STRIKING:
2203 case SPE_FORCE_BOLT:
2204 learn_it = TRUE;
2205 if (Antimagic) {
2206 shieldeff(u.ux, u.uy);
2207 pline("Boing!");
2208 } else {
2209 if (ordinary) {
2210 You("bash yourself!");
2211 damage = d(2, 12);
2212 } else
2213 damage = d(1 + obj->spe, 6);
2214 exercise(A_STR, FALSE);
2216 break;
2218 case WAN_LIGHTNING:
2219 learn_it = TRUE;
2220 if (!Shock_resistance) {
2221 You("shock yourself!");
2222 damage = d(12, 6);
2223 exercise(A_CON, FALSE);
2224 } else {
2225 shieldeff(u.ux, u.uy);
2226 You("zap yourself, but seem unharmed.");
2227 ugolemeffects(AD_ELEC, d(12, 6));
2229 destroy_item(WAND_CLASS, AD_ELEC);
2230 destroy_item(RING_CLASS, AD_ELEC);
2231 (void) flashburn((long) rnd(100));
2232 break;
2234 case SPE_FIREBALL:
2235 You("explode a fireball on top of yourself!");
2236 explode(u.ux, u.uy, 11, d(6, 6), WAND_CLASS, EXPL_FIERY);
2237 break;
2238 case WAN_FIRE:
2239 case FIRE_HORN:
2240 learn_it = TRUE;
2241 if (Fire_resistance) {
2242 shieldeff(u.ux, u.uy);
2243 You_feel("rather warm.");
2244 ugolemeffects(AD_FIRE, d(12, 6));
2245 } else {
2246 pline("You've set yourself afire!");
2247 damage = d(12, 6);
2249 burn_away_slime();
2250 (void) burnarmor(&youmonst);
2251 destroy_item(SCROLL_CLASS, AD_FIRE);
2252 destroy_item(POTION_CLASS, AD_FIRE);
2253 destroy_item(SPBOOK_CLASS, AD_FIRE);
2254 destroy_item(FOOD_CLASS, AD_FIRE); /* only slime for now */
2255 break;
2257 case WAN_COLD:
2258 case SPE_CONE_OF_COLD:
2259 case FROST_HORN:
2260 learn_it = TRUE;
2261 if (Cold_resistance) {
2262 shieldeff(u.ux, u.uy);
2263 You_feel("a little chill.");
2264 ugolemeffects(AD_COLD, d(12, 6));
2265 } else {
2266 You("imitate a popsicle!");
2267 damage = d(12, 6);
2269 destroy_item(POTION_CLASS, AD_COLD);
2270 break;
2272 case WAN_MAGIC_MISSILE:
2273 case SPE_MAGIC_MISSILE:
2274 learn_it = TRUE;
2275 if (Antimagic) {
2276 shieldeff(u.ux, u.uy);
2277 pline_The("missiles bounce!");
2278 } else {
2279 damage = d(4, 6);
2280 pline("Idiot! You've shot yourself!");
2282 break;
2284 case WAN_POLYMORPH:
2285 case SPE_POLYMORPH:
2286 if (!Unchanging) {
2287 learn_it = TRUE;
2288 polyself(0);
2290 break;
2292 case WAN_CANCELLATION:
2293 case SPE_CANCELLATION:
2294 (void) cancel_monst(&youmonst, obj, TRUE, FALSE, TRUE);
2295 break;
2297 case SPE_DRAIN_LIFE:
2298 if (!Drain_resistance) {
2299 learn_it = TRUE; /* (no effect for spells...) */
2300 losexp("life drainage");
2302 damage = 0; /* No additional damage */
2303 break;
2305 case WAN_MAKE_INVISIBLE: {
2306 /* have to test before changing HInvis but must change
2307 * HInvis before doing newsym().
2309 int msg = !Invis && !Blind && !BInvis;
2311 if (BInvis && uarmc->otyp == MUMMY_WRAPPING) {
2312 /* A mummy wrapping absorbs it and protects you */
2313 You_feel("rather itchy under %s.", yname(uarmc));
2314 break;
2316 if (ordinary || !rn2(10)) { /* permanent */
2317 HInvis |= FROMOUTSIDE;
2318 } else { /* temporary */
2319 incr_itimeout(&HInvis, d(obj->spe, 250));
2321 if (msg) {
2322 learn_it = TRUE;
2323 newsym(u.ux, u.uy);
2324 self_invis_message();
2326 break;
2329 case WAN_SPEED_MONSTER:
2330 if (!(HFast & INTRINSIC)) {
2331 learn_it = TRUE;
2332 if (!Fast)
2333 You("speed up.");
2334 else
2335 Your("quickness feels more natural.");
2336 exercise(A_DEX, TRUE);
2338 HFast |= FROMOUTSIDE;
2339 break;
2341 case WAN_SLEEP:
2342 case SPE_SLEEP:
2343 learn_it = TRUE;
2344 if (Sleep_resistance) {
2345 shieldeff(u.ux, u.uy);
2346 You("don't feel sleepy!");
2347 } else {
2348 pline_The("sleep ray hits you!");
2349 fall_asleep(-rnd(50), TRUE);
2351 break;
2353 case WAN_SLOW_MONSTER:
2354 case SPE_SLOW_MONSTER:
2355 if (HFast & (TIMEOUT | INTRINSIC)) {
2356 learn_it = TRUE;
2357 u_slow_down();
2359 break;
2361 case WAN_TELEPORTATION:
2362 case SPE_TELEPORT_AWAY:
2363 tele();
2364 /* same criteria as when mounted (zap_steed) */
2365 if ((Teleport_control && !Stunned) || !couldsee(u.ux0, u.uy0)
2366 || distu(u.ux0, u.uy0) >= 16)
2367 learn_it = TRUE;
2368 break;
2370 case WAN_DEATH:
2371 case SPE_FINGER_OF_DEATH:
2372 if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
2373 pline((obj->otyp == WAN_DEATH)
2374 ? "The wand shoots an apparently harmless beam at you."
2375 : "You seem no deader than before.");
2376 break;
2378 learn_it = TRUE;
2379 Sprintf(killer.name, "shot %sself with a death ray", uhim());
2380 killer.format = NO_KILLER_PREFIX;
2381 You("irradiate yourself with pure energy!");
2382 You("die.");
2383 /* They might survive with an amulet of life saving */
2384 done(DIED);
2385 break;
2386 case WAN_UNDEAD_TURNING:
2387 case SPE_TURN_UNDEAD:
2388 learn_it = TRUE;
2389 (void) unturn_dead(&youmonst);
2390 if (is_undead(youmonst.data)) {
2391 You_feel("frightened and %sstunned.",
2392 Stunned ? "even more " : "");
2393 make_stunned((HStun & TIMEOUT) + (long) rnd(30), FALSE);
2394 } else
2395 You("shudder in dread.");
2396 break;
2397 case SPE_HEALING:
2398 case SPE_EXTRA_HEALING:
2399 learn_it = TRUE; /* (no effect for spells...) */
2400 healup(d(6, obj->otyp == SPE_EXTRA_HEALING ? 8 : 4), 0, FALSE,
2401 (obj->otyp == SPE_EXTRA_HEALING));
2402 You_feel("%sbetter.", obj->otyp == SPE_EXTRA_HEALING ? "much " : "");
2403 break;
2404 case WAN_LIGHT: /* (broken wand) */
2405 /* assert( !ordinary ); */
2406 damage = d(obj->spe, 25);
2407 case EXPENSIVE_CAMERA:
2408 if (!damage)
2409 damage = 5;
2410 damage = lightdamage(obj, ordinary, damage);
2411 damage += rnd(25);
2412 if (flashburn((long) damage))
2413 learn_it = TRUE;
2414 damage = 0; /* reset */
2415 break;
2416 case WAN_OPENING:
2417 case SPE_KNOCK:
2418 if (Punished) {
2419 learn_it = TRUE;
2420 unpunish();
2422 if (u.utrap) { /* escape web or bear trap */
2423 (void) openholdingtrap(&youmonst, &learn_it);
2424 } else {
2425 struct obj *otmp;
2426 /* unlock carried boxes */
2427 for (otmp = invent; otmp; otmp = otmp->nobj)
2428 if (Is_box(otmp))
2429 (void) boxlock(otmp, obj);
2430 /* trigger previously escaped trapdoor */
2431 (void) openfallingtrap(&youmonst, TRUE, &learn_it);
2433 break;
2434 case WAN_LOCKING:
2435 case SPE_WIZARD_LOCK:
2436 if (!u.utrap) {
2437 (void) closeholdingtrap(&youmonst, &learn_it);
2439 break;
2440 case WAN_DIGGING:
2441 case SPE_DIG:
2442 case SPE_DETECT_UNSEEN:
2443 case WAN_NOTHING:
2444 break;
2445 case WAN_PROBING: {
2446 struct obj *otmp;
2448 for (otmp = invent; otmp; otmp = otmp->nobj) {
2449 otmp->dknown = 1;
2450 if (Is_container(otmp) || otmp->otyp == STATUE) {
2451 otmp->lknown = 1;
2452 if (!SchroedingersBox(otmp))
2453 otmp->cknown = 1;
2456 learn_it = TRUE;
2457 ustatusline();
2458 break;
2460 case SPE_STONE_TO_FLESH: {
2461 struct obj *otmp, *onxt;
2462 boolean didmerge;
2464 if (u.umonnum == PM_STONE_GOLEM) {
2465 learn_it = TRUE;
2466 (void) polymon(PM_FLESH_GOLEM);
2468 if (Stoned) {
2469 learn_it = TRUE;
2470 fix_petrification(); /* saved! */
2472 /* but at a cost.. */
2473 for (otmp = invent; otmp; otmp = onxt) {
2474 onxt = otmp->nobj;
2475 if (bhito(otmp, obj))
2476 learn_it = TRUE;
2479 * It is possible that we can now merge some inventory.
2480 * Do a highly paranoid merge. Restart from the beginning
2481 * until no merges.
2483 do {
2484 didmerge = FALSE;
2485 for (otmp = invent; !didmerge && otmp; otmp = otmp->nobj)
2486 for (onxt = otmp->nobj; onxt; onxt = onxt->nobj)
2487 if (merged(&otmp, &onxt)) {
2488 didmerge = TRUE;
2489 break;
2491 } while (didmerge);
2492 break;
2494 default:
2495 impossible("zapyourself: object %d used?", obj->otyp);
2496 break;
2498 /* if effect was observable then discover the wand type provided
2499 that the wand itself has been seen */
2500 if (learn_it)
2501 learnwand(obj);
2502 return damage;
2505 /* called when poly'd hero uses breath attack against self */
2506 void
2507 ubreatheu(mattk)
2508 struct attack *mattk;
2510 int dtyp = 20 + mattk->adtyp - 1; /* breath by hero */
2511 const char *fltxt = flash_types[dtyp]; /* blast of <something> */
2513 zhitu(dtyp, mattk->damn, fltxt, u.ux, u.uy);
2516 /* light damages hero in gremlin form */
2518 lightdamage(obj, ordinary, amt)
2519 struct obj *obj; /* item making light (fake book if spell) */
2520 boolean ordinary; /* wand/camera zap vs wand destruction */
2521 int amt; /* pseudo-damage used to determine blindness duration */
2523 char buf[BUFSZ];
2524 const char *how;
2525 int dmg = amt;
2527 if (dmg && youmonst.data == &mons[PM_GREMLIN]) {
2528 /* reduce high values (from destruction of wand with many charges) */
2529 dmg = rnd(dmg);
2530 if (dmg > 10)
2531 dmg = 10 + rnd(dmg - 10);
2532 if (dmg > 20)
2533 dmg = 20;
2534 pline("Ow, that light hurts%c", (dmg > 2 || u.mh <= 5) ? '!' : '.');
2535 /* [composing killer/reason is superfluous here; if fatal, cause
2536 of death will always be "killed while stuck in creature form"] */
2537 if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS)
2538 ordinary = FALSE; /* say blasted rather than zapped */
2539 how = (obj->oclass != SPBOOK_CLASS)
2540 ? (const char *) ansimpleoname(obj)
2541 : "spell of light";
2542 Sprintf(buf, "%s %sself with %s", ordinary ? "zapped" : "blasted",
2543 uhim(), how);
2544 /* might rehumanize(); could be fatal, but only for Unchanging */
2545 losehp(Maybe_Half_Phys(dmg), buf, NO_KILLER_PREFIX);
2547 return dmg;
2550 /* light[ning] causes blindness */
2551 boolean
2552 flashburn(duration)
2553 long duration;
2555 if (!resists_blnd(&youmonst)) {
2556 You(are_blinded_by_the_flash);
2557 make_blinded(duration, FALSE);
2558 if (!Blind)
2559 Your1(vision_clears);
2560 return TRUE;
2562 return FALSE;
2565 /* you've zapped a wand downwards while riding
2566 * Return TRUE if the steed was hit by the wand.
2567 * Return FALSE if the steed was not hit by the wand.
2569 STATIC_OVL boolean
2570 zap_steed(obj)
2571 struct obj *obj; /* wand or spell */
2573 int steedhit = FALSE;
2575 bhitpos.x = u.usteed->mx, bhitpos.y = u.usteed->my;
2576 notonhead = FALSE;
2577 switch (obj->otyp) {
2579 * Wands that are allowed to hit the steed
2580 * Carefully test the results of any that are
2581 * moved here from the bottom section.
2583 case WAN_PROBING:
2584 probe_monster(u.usteed);
2585 learnwand(obj);
2586 steedhit = TRUE;
2587 break;
2588 case WAN_TELEPORTATION:
2589 case SPE_TELEPORT_AWAY:
2590 /* you go together */
2591 tele();
2592 /* same criteria as when unmounted (zapyourself) */
2593 if ((Teleport_control && !Stunned) || !couldsee(u.ux0, u.uy0)
2594 || distu(u.ux0, u.uy0) >= 16)
2595 learnwand(obj);
2596 steedhit = TRUE;
2597 break;
2599 /* Default processing via bhitm() for these */
2600 case SPE_CURE_SICKNESS:
2601 case WAN_MAKE_INVISIBLE:
2602 case WAN_CANCELLATION:
2603 case SPE_CANCELLATION:
2604 case WAN_POLYMORPH:
2605 case SPE_POLYMORPH:
2606 case WAN_STRIKING:
2607 case SPE_FORCE_BOLT:
2608 case WAN_SLOW_MONSTER:
2609 case SPE_SLOW_MONSTER:
2610 case WAN_SPEED_MONSTER:
2611 case SPE_HEALING:
2612 case SPE_EXTRA_HEALING:
2613 case SPE_DRAIN_LIFE:
2614 case WAN_OPENING:
2615 case SPE_KNOCK:
2616 (void) bhitm(u.usteed, obj);
2617 steedhit = TRUE;
2618 break;
2620 default:
2621 steedhit = FALSE;
2622 break;
2624 return steedhit;
2628 * cancel a monster (possibly the hero). inventory is cancelled only
2629 * if the monster is zapping itself directly, since otherwise the
2630 * effect is too strong. currently non-hero monsters do not zap
2631 * themselves with cancellation.
2633 boolean
2634 cancel_monst(mdef, obj, youattack, allow_cancel_kill, self_cancel)
2635 register struct monst *mdef;
2636 register struct obj *obj;
2637 boolean youattack, allow_cancel_kill, self_cancel;
2639 boolean youdefend = (mdef == &youmonst);
2640 static const char writing_vanishes[] =
2641 "Some writing vanishes from %s head!";
2642 static const char your[] = "your"; /* should be extern */
2644 if (youdefend ? (!youattack && Antimagic)
2645 : resist(mdef, obj->oclass, 0, NOTELL))
2646 return FALSE; /* resisted cancellation */
2648 if (self_cancel) { /* 1st cancel inventory */
2649 struct obj *otmp;
2651 for (otmp = (youdefend ? invent : mdef->minvent); otmp;
2652 otmp = otmp->nobj)
2653 cancel_item(otmp);
2654 if (youdefend) {
2655 context.botl = 1; /* potential AC change */
2656 find_ac();
2660 /* now handle special cases */
2661 if (youdefend) {
2662 if (Upolyd) {
2663 if ((u.umonnum == PM_CLAY_GOLEM) && !Blind)
2664 pline(writing_vanishes, your);
2666 if (Unchanging)
2667 Your("amulet grows hot for a moment, then cools.");
2668 else
2669 rehumanize();
2671 } else {
2672 mdef->mcan = TRUE;
2674 if (is_were(mdef->data) && mdef->data->mlet != S_HUMAN)
2675 were_change(mdef);
2677 if (mdef->data == &mons[PM_CLAY_GOLEM]) {
2678 if (canseemon(mdef))
2679 pline(writing_vanishes, s_suffix(mon_nam(mdef)));
2681 if (allow_cancel_kill) {
2682 if (youattack)
2683 killed(mdef);
2684 else
2685 monkilled(mdef, "", AD_SPEL);
2689 return TRUE;
2692 /* you've zapped an immediate type wand up or down */
2693 STATIC_OVL boolean
2694 zap_updown(obj)
2695 struct obj *obj; /* wand or spell */
2697 boolean striking = FALSE, disclose = FALSE;
2698 int x, y, xx, yy, ptmp;
2699 struct obj *otmp;
2700 struct engr *e;
2701 struct trap *ttmp;
2702 char buf[BUFSZ];
2704 /* some wands have special effects other than normal bhitpile */
2705 /* drawbridge might change <u.ux,u.uy> */
2706 x = xx = u.ux; /* <x,y> is zap location */
2707 y = yy = u.uy; /* <xx,yy> is drawbridge (portcullis) position */
2708 ttmp = t_at(x, y); /* trap if there is one */
2710 switch (obj->otyp) {
2711 case WAN_PROBING:
2712 ptmp = 0;
2713 if (u.dz < 0) {
2714 You("probe towards the %s.", ceiling(x, y));
2715 } else {
2716 ptmp += bhitpile(obj, bhito, x, y, u.dz);
2717 You("probe beneath the %s.", surface(x, y));
2718 ptmp += display_binventory(x, y, TRUE);
2720 if (!ptmp)
2721 Your("probe reveals nothing.");
2722 return TRUE; /* we've done our own bhitpile */
2723 case WAN_OPENING:
2724 case SPE_KNOCK:
2725 /* up or down, but at closed portcullis only */
2726 if (is_db_wall(x, y) && find_drawbridge(&xx, &yy)) {
2727 open_drawbridge(xx, yy);
2728 disclose = TRUE;
2729 } else if (u.dz > 0 && (x == xdnstair && y == ydnstair)
2730 /* can't use the stairs down to quest level 2 until
2731 leader "unlocks" them; give feedback if you try */
2732 && on_level(&u.uz, &qstart_level) && !ok_to_quest()) {
2733 pline_The("stairs seem to ripple momentarily.");
2734 disclose = TRUE;
2736 /* down will release you from bear trap or web */
2737 if (u.dz > 0 && u.utrap) {
2738 (void) openholdingtrap(&youmonst, &disclose);
2739 /* down will trigger trapdoor, hole, or [spiked-] pit */
2740 } else if (u.dz > 0 && !u.utrap) {
2741 (void) openfallingtrap(&youmonst, FALSE, &disclose);
2743 break;
2744 case WAN_STRIKING:
2745 case SPE_FORCE_BOLT:
2746 striking = TRUE;
2747 /*FALLTHRU*/
2748 case WAN_LOCKING:
2749 case SPE_WIZARD_LOCK:
2750 /* down at open bridge or up or down at open portcullis */
2751 if (((levl[x][y].typ == DRAWBRIDGE_DOWN)
2752 ? (u.dz > 0)
2753 : (is_drawbridge_wall(x, y) >= 0 && !is_db_wall(x, y)))
2754 && find_drawbridge(&xx, &yy)) {
2755 if (!striking)
2756 close_drawbridge(xx, yy);
2757 else
2758 destroy_drawbridge(xx, yy);
2759 disclose = TRUE;
2760 } else if (striking && u.dz < 0 && rn2(3) && !Is_airlevel(&u.uz)
2761 && !Is_waterlevel(&u.uz) && !Underwater
2762 && !Is_qstart(&u.uz)) {
2763 int dmg;
2764 /* similar to zap_dig() */
2765 pline("A rock is dislodged from the %s and falls on your %s.",
2766 ceiling(x, y), body_part(HEAD));
2767 dmg = rnd((uarmh && is_metallic(uarmh)) ? 2 : 6);
2768 losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN);
2769 if ((otmp = mksobj_at(ROCK, x, y, FALSE, FALSE)) != 0) {
2770 (void) xname(otmp); /* set dknown, maybe bknown */
2771 stackobj(otmp);
2773 newsym(x, y);
2774 } else if (u.dz > 0 && ttmp) {
2775 if (!striking && closeholdingtrap(&youmonst, &disclose)) {
2776 ; /* now stuck in web or bear trap */
2777 } else if (striking && ttmp->ttyp == TRAPDOOR) {
2778 /* striking transforms trapdoor into hole */
2779 if (Blind && !ttmp->tseen) {
2780 pline("%s beneath you shatters.", Something);
2781 } else if (!ttmp->tseen) { /* => !Blind */
2782 pline("There's a trapdoor beneath you; it shatters.");
2783 } else {
2784 pline("The trapdoor beneath you shatters.");
2785 disclose = TRUE;
2787 ttmp->ttyp = HOLE;
2788 ttmp->tseen = 1;
2789 newsym(x, y);
2790 /* might fall down hole */
2791 dotrap(ttmp, 0);
2792 } else if (!striking && ttmp->ttyp == HOLE) {
2793 /* locking transforms hole into trapdoor */
2794 ttmp->ttyp = TRAPDOOR;
2795 if (Blind || !ttmp->tseen) {
2796 pline("Some %s swirls beneath you.",
2797 is_ice(x, y) ? "frost" : "dust");
2798 } else {
2799 ttmp->tseen = 1;
2800 newsym(x, y);
2801 pline("A trapdoor appears beneath you.");
2802 disclose = TRUE;
2804 /* hadn't fallen down hole; won't fall now */
2807 break;
2808 case SPE_STONE_TO_FLESH:
2809 if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) || Underwater
2810 || (Is_qstart(&u.uz) && u.dz < 0)) {
2811 pline1(nothing_happens);
2812 } else if (u.dz < 0) { /* we should do more... */
2813 pline("Blood drips on your %s.", body_part(FACE));
2814 } else if (u.dz > 0 && !OBJ_AT(u.ux, u.uy)) {
2816 Print this message only if there wasn't an engraving
2817 affected here. If water or ice, act like waterlevel case.
2819 e = engr_at(u.ux, u.uy);
2820 if (!(e && e->engr_type == ENGRAVE)) {
2821 if (is_pool(u.ux, u.uy) || is_ice(u.ux, u.uy))
2822 pline1(nothing_happens);
2823 else
2824 pline("Blood %ss %s your %s.",
2825 is_lava(u.ux, u.uy) ? "boil" : "pool",
2826 Levitation ? "beneath" : "at",
2827 makeplural(body_part(FOOT)));
2830 break;
2831 default:
2832 break;
2835 if (u.dz > 0) {
2836 /* zapping downward */
2837 (void) bhitpile(obj, bhito, x, y, u.dz);
2839 /* subset of engraving effects; none sets `disclose' */
2840 if ((e = engr_at(x, y)) != 0 && e->engr_type != HEADSTONE) {
2841 switch (obj->otyp) {
2842 case WAN_POLYMORPH:
2843 case SPE_POLYMORPH:
2844 del_engr(e);
2845 make_engr_at(x, y, random_engraving(buf), moves, (xchar) 0);
2846 break;
2847 case WAN_CANCELLATION:
2848 case SPE_CANCELLATION:
2849 case WAN_MAKE_INVISIBLE:
2850 del_engr(e);
2851 break;
2852 case WAN_TELEPORTATION:
2853 case SPE_TELEPORT_AWAY:
2854 rloc_engr(e);
2855 break;
2856 case SPE_STONE_TO_FLESH:
2857 if (e->engr_type == ENGRAVE) {
2858 /* only affects things in stone */
2859 pline_The(Hallucination
2860 ? "floor runs like butter!"
2861 : "edges on the floor get smoother.");
2862 wipe_engr_at(x, y, d(2, 4), TRUE);
2864 break;
2865 case WAN_STRIKING:
2866 case SPE_FORCE_BOLT:
2867 wipe_engr_at(x, y, d(2, 4), TRUE);
2868 break;
2869 default:
2870 break;
2873 } else if (u.dz < 0) {
2874 /* zapping upward */
2876 /* game flavor: if you're hiding under "something"
2877 * a zap upward should hit that "something".
2879 if (u.uundetected && hides_under(youmonst.data)) {
2880 int hitit = 0;
2881 otmp = level.objects[u.ux][u.uy];
2883 if (otmp)
2884 hitit = bhito(otmp, obj);
2885 if (hitit) {
2886 (void) hideunder(&youmonst);
2887 disclose = TRUE;
2892 return disclose;
2895 /* used by do_break_wand() was well as by weffects() */
2896 void
2897 zapsetup()
2899 obj_zapped = FALSE;
2902 void
2903 zapwrapup()
2905 /* if do_osshock() set obj_zapped while polying, give a message now */
2906 if (obj_zapped)
2907 You_feel("shuddering vibrations.");
2908 obj_zapped = FALSE;
2911 /* called for various wand and spell effects - M. Stephenson */
2912 void
2913 weffects(obj)
2914 struct obj *obj;
2916 int otyp = obj->otyp;
2917 boolean disclose = FALSE, was_unkn = !objects[otyp].oc_name_known;
2919 exercise(A_WIS, TRUE);
2920 if (u.usteed && (objects[otyp].oc_dir != NODIR) && !u.dx && !u.dy
2921 && (u.dz > 0) && zap_steed(obj)) {
2922 disclose = TRUE;
2923 } else if (objects[otyp].oc_dir == IMMEDIATE) {
2924 zapsetup(); /* reset obj_zapped */
2925 if (u.uswallow) {
2926 (void) bhitm(u.ustuck, obj);
2927 /* [how about `bhitpile(u.ustuck->minvent)' effect?] */
2928 } else if (u.dz) {
2929 disclose = zap_updown(obj);
2930 } else {
2931 (void) bhit(u.dx, u.dy, rn1(8, 6), ZAPPED_WAND, bhitm, bhito,
2932 &obj);
2934 zapwrapup(); /* give feedback for obj_zapped */
2936 } else if (objects[otyp].oc_dir == NODIR) {
2937 zapnodir(obj);
2939 } else {
2940 /* neither immediate nor directionless */
2942 if (otyp == WAN_DIGGING || otyp == SPE_DIG)
2943 zap_dig();
2944 else if (otyp >= SPE_MAGIC_MISSILE && otyp <= SPE_FINGER_OF_DEATH)
2945 buzz(otyp - SPE_MAGIC_MISSILE + 10, u.ulevel / 2 + 1, u.ux, u.uy,
2946 u.dx, u.dy);
2947 else if (otyp >= WAN_MAGIC_MISSILE && otyp <= WAN_LIGHTNING)
2948 buzz(otyp - WAN_MAGIC_MISSILE,
2949 (otyp == WAN_MAGIC_MISSILE) ? 2 : 6, u.ux, u.uy, u.dx, u.dy);
2950 else
2951 impossible("weffects: unexpected spell or wand");
2952 disclose = TRUE;
2954 if (disclose) {
2955 learnwand(obj);
2956 if (was_unkn)
2957 more_experienced(0, 10);
2959 return;
2962 /* augment damage for a spell dased on the hero's intelligence (and level) */
2964 spell_damage_bonus(dmg)
2965 int dmg; /* base amount to be adjusted by bonus or penalty */
2967 int intell = ACURR(A_INT);
2969 /* Punish low intelligence before low level else low intelligence
2970 gets punished only when high level */
2971 if (intell <= 9) {
2972 /* -3 penalty, but never reduce combined amount below 1
2973 (if dmg is 0 for some reason, we're careful to leave it there) */
2974 if (dmg > 1)
2975 dmg = (dmg <= 3) ? 1 : dmg - 3;
2976 } else if (intell <= 13 || u.ulevel < 5)
2977 ; /* no bonus or penalty; dmg remains same */
2978 else if (intell <= 18)
2979 dmg += 1;
2980 else if (intell <= 24 || u.ulevel < 14)
2981 dmg += 2;
2982 else
2983 dmg += 3; /* Int 25 */
2985 return dmg;
2989 * Generate the to hit bonus for a spell. Based on the hero's skill in
2990 * spell class and dexterity.
2992 STATIC_OVL int
2993 spell_hit_bonus(skill)
2994 int skill;
2996 int hit_bon = 0;
2997 int dex = ACURR(A_DEX);
2999 switch (P_SKILL(spell_skilltype(skill))) {
3000 case P_ISRESTRICTED:
3001 case P_UNSKILLED:
3002 hit_bon = -4;
3003 break;
3004 case P_BASIC:
3005 hit_bon = 0;
3006 break;
3007 case P_SKILLED:
3008 hit_bon = 2;
3009 break;
3010 case P_EXPERT:
3011 hit_bon = 3;
3012 break;
3015 if (dex < 4)
3016 hit_bon -= 3;
3017 else if (dex < 6)
3018 hit_bon -= 2;
3019 else if (dex < 8)
3020 hit_bon -= 1;
3021 else if (dex < 14)
3022 /* Will change when print stuff below removed */
3023 hit_bon -= 0;
3024 else
3025 /* Even increment for dextrous heroes (see weapon.c abon) */
3026 hit_bon += dex - 14;
3028 return hit_bon;
3031 const char *
3032 exclam(force)
3033 int force;
3035 /* force == 0 occurs e.g. with sleep ray */
3036 /* note that large force is usual with wands so that !! would
3037 require information about hand/weapon/wand */
3038 return (const char *) ((force < 0) ? "?" : (force <= 4) ? "." : "!");
3041 void
3042 hit(str, mtmp, force)
3043 const char *str;
3044 struct monst *mtmp;
3045 const char *force; /* usually either "." or "!" */
3047 if ((!cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp)
3048 && !(u.uswallow && mtmp == u.ustuck)) || !flags.verbose)
3049 pline("%s %s it.", The(str), vtense(str, "hit"));
3050 else
3051 pline("%s %s %s%s", The(str), vtense(str, "hit"),
3052 mon_nam(mtmp), force);
3055 void
3056 miss(str, mtmp)
3057 register const char *str;
3058 register struct monst *mtmp;
3060 pline(
3061 "%s %s %s.", The(str), vtense(str, "miss"),
3062 ((cansee(bhitpos.x, bhitpos.y) || canspotmon(mtmp)) && flags.verbose)
3063 ? mon_nam(mtmp)
3064 : "it");
3067 STATIC_OVL void
3068 skiprange(range, skipstart, skipend)
3069 int range, *skipstart, *skipend;
3071 int tr = (range / 4);
3072 int tmp = range - ((tr > 0) ? rnd(tr) : 0);
3073 *skipstart = tmp;
3074 *skipend = tmp - ((tmp / 4) * rnd(3));
3075 if (*skipend >= tmp)
3076 *skipend = tmp - 1;
3080 * Called for the following distance effects:
3081 * when a weapon is thrown (weapon == THROWN_WEAPON)
3082 * when an object is kicked (KICKED_WEAPON)
3083 * when an IMMEDIATE wand is zapped (ZAPPED_WAND)
3084 * when a light beam is flashed (FLASHED_LIGHT)
3085 * when a mirror is applied (INVIS_BEAM)
3086 * A thrown/kicked object falls down at end of its range or when a monster
3087 * is hit. The variable 'bhitpos' is set to the final position of the weapon
3088 * thrown/zapped. The ray of a wand may affect (by calling a provided
3089 * function) several objects and monsters on its path. The return value
3090 * is the monster hit (weapon != ZAPPED_WAND), or a null monster pointer.
3092 * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be
3093 * destroyed and *pobj set to NULL to indicate this.
3095 * Check !u.uswallow before calling bhit().
3096 * This function reveals the absence of a remembered invisible monster in
3097 * necessary cases (throwing or kicking weapons). The presence of a real
3098 * one is revealed for a weapon, but if not a weapon is left up to fhitm().
3100 struct monst *
3101 bhit(ddx, ddy, range, weapon, fhitm, fhito, pobj)
3102 register int ddx, ddy, range; /* direction and range */
3103 int weapon; /* see values in hack.h */
3104 int FDECL((*fhitm), (MONST_P, OBJ_P)), /* fns called when mon/obj hit */
3105 FDECL((*fhito), (OBJ_P, OBJ_P));
3106 struct obj **pobj; /* object tossed/used, set to NULL
3107 * if object is destroyed */
3109 struct monst *mtmp;
3110 struct obj *obj = *pobj;
3111 uchar typ;
3112 boolean shopdoor = FALSE, point_blank = TRUE;
3113 boolean in_skip = FALSE, allow_skip = FALSE;
3114 int skiprange_start = 0, skiprange_end = 0, skipcount = 0;
3116 if (weapon == KICKED_WEAPON) {
3117 /* object starts one square in front of player */
3118 bhitpos.x = u.ux + ddx;
3119 bhitpos.y = u.uy + ddy;
3120 range--;
3121 } else {
3122 bhitpos.x = u.ux;
3123 bhitpos.y = u.uy;
3126 if (weapon == THROWN_WEAPON && obj && obj->otyp == ROCK) {
3127 skiprange(range, &skiprange_start, &skiprange_end);
3128 allow_skip = !rn2(3);
3131 if (weapon == FLASHED_LIGHT) {
3132 tmp_at(DISP_BEAM, cmap_to_glyph(S_flashbeam));
3133 } else if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
3134 tmp_at(DISP_FLASH, obj_to_glyph(obj));
3136 while (range-- > 0) {
3137 int x, y;
3139 bhitpos.x += ddx;
3140 bhitpos.y += ddy;
3141 x = bhitpos.x;
3142 y = bhitpos.y;
3144 if (!isok(x, y)) {
3145 bhitpos.x -= ddx;
3146 bhitpos.y -= ddy;
3147 break;
3150 if (is_pick(obj) && inside_shop(x, y)
3151 && (mtmp = shkcatch(obj, x, y)) != 0) {
3152 tmp_at(DISP_END, 0);
3153 return mtmp;
3156 typ = levl[bhitpos.x][bhitpos.y].typ;
3158 /* iron bars will block anything big enough */
3159 if ((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
3160 && typ == IRONBARS
3161 && hits_bars(pobj, x - ddx, y - ddy, bhitpos.x, bhitpos.y,
3162 point_blank ? 0 : !rn2(5), 1)) {
3163 /* caveat: obj might now be null... */
3164 obj = *pobj;
3165 bhitpos.x -= ddx;
3166 bhitpos.y -= ddy;
3167 break;
3170 if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) {
3171 boolean learn_it = FALSE;
3173 switch (obj->otyp) {
3174 case WAN_OPENING:
3175 case SPE_KNOCK:
3176 if (is_db_wall(bhitpos.x, bhitpos.y)) {
3177 if (cansee(x, y) || cansee(bhitpos.x, bhitpos.y))
3178 learn_it = TRUE;
3179 open_drawbridge(x, y);
3181 break;
3182 case WAN_LOCKING:
3183 case SPE_WIZARD_LOCK:
3184 if ((cansee(x, y) || cansee(bhitpos.x, bhitpos.y))
3185 && levl[x][y].typ == DRAWBRIDGE_DOWN)
3186 learn_it = TRUE;
3187 close_drawbridge(x, y);
3188 break;
3189 case WAN_STRIKING:
3190 case SPE_FORCE_BOLT:
3191 if (typ != DRAWBRIDGE_UP)
3192 destroy_drawbridge(x, y);
3193 learn_it = TRUE;
3194 break;
3196 if (learn_it)
3197 learnwand(obj);
3200 mtmp = m_at(bhitpos.x, bhitpos.y);
3203 * skipping rocks
3205 * skiprange_start is only set if this is a thrown rock
3207 if (skiprange_start && (range == skiprange_start) && allow_skip) {
3208 if (is_pool(bhitpos.x, bhitpos.y) && !mtmp) {
3209 in_skip = TRUE;
3210 if (!Blind)
3211 pline("%s %s%s.", Yname2(obj), otense(obj, "skip"),
3212 skipcount ? " again" : "");
3213 else
3214 You_hear("%s skip.", yname(obj));
3215 skipcount++;
3216 } else if (skiprange_start > skiprange_end + 1) {
3217 --skiprange_start;
3220 if (in_skip) {
3221 if (range <= skiprange_end) {
3222 in_skip = FALSE;
3223 if (range > 3) /* another bounce? */
3224 skiprange(range, &skiprange_start, &skiprange_end);
3225 } else if (mtmp && M_IN_WATER(mtmp->data)) {
3226 if ((!Blind && canseemon(mtmp)) || sensemon(mtmp))
3227 pline("%s %s over %s.", Yname2(obj), otense(obj, "pass"),
3228 mon_nam(mtmp));
3232 if (mtmp && !(in_skip && M_IN_WATER(mtmp->data))) {
3233 notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
3234 if (weapon == FLASHED_LIGHT) {
3235 /* FLASHED_LIGHT hitting invisible monster should
3236 pass through instead of stop so we call
3237 flash_hits_mon() directly rather than returning
3238 mtmp back to caller. That allows the flash to
3239 keep on going. Note that we use mtmp->minvis
3240 not canspotmon() because it makes no difference
3241 whether the hero can see the monster or not. */
3242 if (mtmp->minvis) {
3243 obj->ox = u.ux, obj->oy = u.uy;
3244 (void) flash_hits_mon(mtmp, obj);
3245 } else {
3246 tmp_at(DISP_END, 0);
3247 return mtmp; /* caller will call flash_hits_mon */
3249 } else if (weapon == INVIS_BEAM) {
3250 /* Like FLASHED_LIGHT, INVIS_BEAM should continue
3251 through invisible targets; unlike it, we aren't
3252 prepared for multiple hits so just get first one
3253 that's either visible or could see its invisible
3254 self. [No tmp_at() cleanup is needed here.] */
3255 if (!mtmp->minvis || perceives(mtmp->data))
3256 return mtmp;
3257 } else if (weapon != ZAPPED_WAND) {
3258 /* THROWN_WEAPON, KICKED_WEAPON */
3259 tmp_at(DISP_END, 0);
3260 if (cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp))
3261 map_invisible(bhitpos.x, bhitpos.y);
3262 return mtmp;
3263 } else {
3264 /* ZAPPED_WAND */
3265 (*fhitm)(mtmp, obj);
3266 range -= 3;
3268 } else {
3269 if (weapon == ZAPPED_WAND && obj->otyp == WAN_PROBING
3270 && glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)) {
3271 unmap_object(bhitpos.x, bhitpos.y);
3272 newsym(x, y);
3275 if (fhito) {
3276 if (bhitpile(obj, fhito, bhitpos.x, bhitpos.y, 0))
3277 range--;
3278 } else {
3279 if (weapon == KICKED_WEAPON
3280 && ((obj->oclass == COIN_CLASS
3281 && OBJ_AT(bhitpos.x, bhitpos.y))
3282 || ship_object(obj, bhitpos.x, bhitpos.y,
3283 costly_spot(bhitpos.x, bhitpos.y)))) {
3284 tmp_at(DISP_END, 0);
3285 return (struct monst *) 0;
3288 if (weapon == ZAPPED_WAND && (IS_DOOR(typ) || typ == SDOOR)) {
3289 switch (obj->otyp) {
3290 case WAN_OPENING:
3291 case WAN_LOCKING:
3292 case WAN_STRIKING:
3293 case SPE_KNOCK:
3294 case SPE_WIZARD_LOCK:
3295 case SPE_FORCE_BOLT:
3296 if (doorlock(obj, bhitpos.x, bhitpos.y)) {
3297 if (cansee(bhitpos.x, bhitpos.y)
3298 || (obj->otyp == WAN_STRIKING && !Deaf))
3299 learnwand(obj);
3300 if (levl[bhitpos.x][bhitpos.y].doormask == D_BROKEN
3301 && *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE)) {
3302 shopdoor = TRUE;
3303 add_damage(bhitpos.x, bhitpos.y, 400L);
3306 break;
3309 if (!ZAP_POS(typ) || closed_door(bhitpos.x, bhitpos.y)) {
3310 bhitpos.x -= ddx;
3311 bhitpos.y -= ddy;
3312 break;
3314 if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) {
3315 /* 'I' present but no monster: erase */
3316 /* do this before the tmp_at() */
3317 if (glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)
3318 && cansee(x, y)) {
3319 unmap_object(bhitpos.x, bhitpos.y);
3320 newsym(x, y);
3322 tmp_at(bhitpos.x, bhitpos.y);
3323 delay_output();
3324 /* kicked objects fall in pools */
3325 if ((weapon == KICKED_WEAPON)
3326 && (is_pool(bhitpos.x, bhitpos.y)
3327 || is_lava(bhitpos.x, bhitpos.y)))
3328 break;
3329 if (IS_SINK(typ) && weapon != FLASHED_LIGHT)
3330 break; /* physical objects fall onto sink */
3332 /* limit range of ball so hero won't make an invalid move */
3333 if (weapon == THROWN_WEAPON && range > 0
3334 && obj->otyp == HEAVY_IRON_BALL) {
3335 struct obj *bobj;
3336 struct trap *t;
3338 if ((bobj = sobj_at(BOULDER, x, y)) != 0) {
3339 if (cansee(x, y))
3340 pline("%s hits %s.", The(distant_name(obj, xname)),
3341 an(xname(bobj)));
3342 range = 0;
3343 } else if (obj == uball) {
3344 if (!test_move(x - ddx, y - ddy, ddx, ddy, TEST_MOVE)) {
3345 /* nb: it didn't hit anything directly */
3346 if (cansee(x, y))
3347 pline("%s jerks to an abrupt halt.",
3348 The(distant_name(obj, xname))); /* lame */
3349 range = 0;
3350 } else if (Sokoban && (t = t_at(x, y)) != 0
3351 && (t->ttyp == PIT || t->ttyp == SPIKED_PIT
3352 || t->ttyp == HOLE || t->ttyp == TRAPDOOR)) {
3353 /* hero falls into the trap, so ball stops */
3354 range = 0;
3359 /* thrown/kicked missile has moved away from its starting spot */
3360 point_blank = FALSE; /* affects passing through iron bars */
3363 if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
3364 tmp_at(DISP_END, 0);
3366 if (shopdoor)
3367 pay_for_damage("destroy", FALSE);
3369 return (struct monst *) 0;
3372 /* process thrown boomerang, which travels a curving path...
3373 * A multi-shot volley ought to have all missiles in flight at once,
3374 * but we're called separately for each one. We terminate the volley
3375 * early on a failed catch since continuing to throw after being hit
3376 * is too obviously silly.
3378 struct monst *
3379 boomhit(obj, dx, dy)
3380 struct obj *obj;
3381 int dx, dy;
3383 register int i, ct;
3384 int boom; /* showsym[] index */
3385 struct monst *mtmp;
3386 boolean counterclockwise = TRUE; /* right-handed throw */
3388 /* counterclockwise traversal patterns:
3389 * ..........................54.................................
3390 * ..................43.....6..3....765.........................
3391 * ..........32.....5..2...7...2...8...4....87..................
3392 * .........4..1....6..1...8..1....9...3...9..6.....98..........
3393 * ..21@....5...@...7..@....9@......@12....@...5...@..7.....@9..
3394 * .3...9....6..9....89.....................1..4...1..6....1..8.
3395 * .4...8.....78.............................23....2..5...2...7.
3396 * ..567............................................34....3..6..
3397 * ........................................................45...
3398 * (invert rows for corresponding clockwise patterns)
3401 bhitpos.x = u.ux;
3402 bhitpos.y = u.uy;
3403 boom = counterclockwise ? S_boomleft : S_boomright;
3404 for (i = 0; i < 8; i++)
3405 if (xdir[i] == dx && ydir[i] == dy)
3406 break;
3407 tmp_at(DISP_FLASH, cmap_to_glyph(boom));
3408 for (ct = 0; ct < 10; ct++) {
3409 i = (i + 8) % 8; /* 0..7 (8 -> 0, -1 -> 7) */
3410 boom = (S_boomleft + S_boomright - boom); /* toggle */
3411 tmp_at(DISP_CHANGE, cmap_to_glyph(boom)); /* change glyph */
3412 dx = xdir[i];
3413 dy = ydir[i];
3414 bhitpos.x += dx;
3415 bhitpos.y += dy;
3416 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
3417 m_respond(mtmp);
3418 tmp_at(DISP_END, 0);
3419 return mtmp;
3421 if (!ZAP_POS(levl[bhitpos.x][bhitpos.y].typ)
3422 || closed_door(bhitpos.x, bhitpos.y)) {
3423 bhitpos.x -= dx;
3424 bhitpos.y -= dy;
3425 break;
3427 if (bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */
3428 if (Fumbling || rn2(20) >= ACURR(A_DEX)) {
3429 /* we hit ourselves */
3430 (void) thitu(10 + obj->spe, dmgval(obj, &youmonst), obj,
3431 "boomerang");
3432 endmultishot(TRUE);
3433 break;
3434 } else { /* we catch it */
3435 tmp_at(DISP_END, 0);
3436 You("skillfully catch the boomerang.");
3437 return &youmonst;
3440 tmp_at(bhitpos.x, bhitpos.y);
3441 delay_output();
3442 if (IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) {
3443 if (!Deaf)
3444 pline("Klonk!");
3445 break; /* boomerang falls on sink */
3447 /* ct==0, initial position, we want next delta to be same;
3448 ct==5, opposite position, repeat delta undoes first one */
3449 if (ct % 5 != 0)
3450 i += (counterclockwise ? -1 : 1);
3452 tmp_at(DISP_END, 0); /* do not leave last symbol */
3453 return (struct monst *) 0;
3456 /* used by buzz(); also used by munslime(muse.c); returns damage applied
3457 to mon; note: caller is responsible for killing mon if damage is fatal */
3459 zhitm(mon, type, nd, ootmp)
3460 register struct monst *mon;
3461 register int type, nd;
3462 struct obj **ootmp; /* to return worn armor for caller to disintegrate */
3464 register int tmp = 0;
3465 register int abstype = abs(type) % 10;
3466 boolean sho_shieldeff = FALSE;
3467 boolean spellcaster = is_hero_spell(type); /* maybe get a bonus! */
3469 *ootmp = (struct obj *) 0;
3470 switch (abstype) {
3471 case ZT_MAGIC_MISSILE:
3472 if (resists_magm(mon)) {
3473 sho_shieldeff = TRUE;
3474 break;
3476 tmp = d(nd, 6);
3477 if (spellcaster)
3478 tmp = spell_damage_bonus(tmp);
3479 break;
3480 case ZT_FIRE:
3481 if (resists_fire(mon)) {
3482 sho_shieldeff = TRUE;
3483 break;
3485 tmp = d(nd, 6);
3486 if (resists_cold(mon))
3487 tmp += 7;
3488 if (spellcaster)
3489 tmp = spell_damage_bonus(tmp);
3490 if (burnarmor(mon)) {
3491 if (!rn2(3))
3492 (void) destroy_mitem(mon, POTION_CLASS, AD_FIRE);
3493 if (!rn2(3))
3494 (void) destroy_mitem(mon, SCROLL_CLASS, AD_FIRE);
3495 if (!rn2(5))
3496 (void) destroy_mitem(mon, SPBOOK_CLASS, AD_FIRE);
3497 destroy_mitem(mon, FOOD_CLASS, AD_FIRE); /* carried slime */
3499 break;
3500 case ZT_COLD:
3501 if (resists_cold(mon)) {
3502 sho_shieldeff = TRUE;
3503 break;
3505 tmp = d(nd, 6);
3506 if (resists_fire(mon))
3507 tmp += d(nd, 3);
3508 if (spellcaster)
3509 tmp = spell_damage_bonus(tmp);
3510 if (!rn2(3))
3511 (void) destroy_mitem(mon, POTION_CLASS, AD_COLD);
3512 break;
3513 case ZT_SLEEP:
3514 tmp = 0;
3515 (void) sleep_monst(mon, d(nd, 25),
3516 type == ZT_WAND(ZT_SLEEP) ? WAND_CLASS : '\0');
3517 break;
3518 case ZT_DEATH: /* death/disintegration */
3519 if (abs(type) != ZT_BREATH(ZT_DEATH)) { /* death */
3520 if (mon->data == &mons[PM_DEATH]) {
3521 mon->mhpmax += mon->mhpmax / 2;
3522 if (mon->mhpmax >= MAGIC_COOKIE)
3523 mon->mhpmax = MAGIC_COOKIE - 1;
3524 mon->mhp = mon->mhpmax;
3525 tmp = 0;
3526 break;
3528 if (nonliving(mon->data) || is_demon(mon->data)
3529 || is_vampshifter(mon) || resists_magm(mon)) {
3530 /* similar to player */
3531 sho_shieldeff = TRUE;
3532 break;
3534 type = -1; /* so they don't get saving throws */
3535 } else {
3536 struct obj *otmp2;
3538 if (resists_disint(mon)) {
3539 sho_shieldeff = TRUE;
3540 } else if (mon->misc_worn_check & W_ARMS) {
3541 /* destroy shield; victim survives */
3542 *ootmp = which_armor(mon, W_ARMS);
3543 } else if (mon->misc_worn_check & W_ARM) {
3544 /* destroy body armor, also cloak if present */
3545 *ootmp = which_armor(mon, W_ARM);
3546 if ((otmp2 = which_armor(mon, W_ARMC)) != 0)
3547 m_useup(mon, otmp2);
3548 } else {
3549 /* no body armor, victim dies; destroy cloak
3550 and shirt now in case target gets life-saved */
3551 tmp = MAGIC_COOKIE;
3552 if ((otmp2 = which_armor(mon, W_ARMC)) != 0)
3553 m_useup(mon, otmp2);
3554 if ((otmp2 = which_armor(mon, W_ARMU)) != 0)
3555 m_useup(mon, otmp2);
3557 type = -1; /* no saving throw wanted */
3558 break; /* not ordinary damage */
3560 tmp = mon->mhp + 1;
3561 break;
3562 case ZT_LIGHTNING:
3563 if (resists_elec(mon)) {
3564 sho_shieldeff = TRUE;
3565 tmp = 0;
3566 /* can still blind the monster */
3567 } else
3568 tmp = d(nd, 6);
3569 if (spellcaster)
3570 tmp = spell_damage_bonus(tmp);
3571 if (!resists_blnd(mon)
3572 && !(type > 0 && u.uswallow && mon == u.ustuck)) {
3573 register unsigned rnd_tmp = rnd(50);
3574 mon->mcansee = 0;
3575 if ((mon->mblinded + rnd_tmp) > 127)
3576 mon->mblinded = 127;
3577 else
3578 mon->mblinded += rnd_tmp;
3580 if (!rn2(3))
3581 (void) destroy_mitem(mon, WAND_CLASS, AD_ELEC);
3582 /* not actually possible yet */
3583 if (!rn2(3))
3584 (void) destroy_mitem(mon, RING_CLASS, AD_ELEC);
3585 break;
3586 case ZT_POISON_GAS:
3587 if (resists_poison(mon)) {
3588 sho_shieldeff = TRUE;
3589 break;
3591 tmp = d(nd, 6);
3592 break;
3593 case ZT_ACID:
3594 if (resists_acid(mon)) {
3595 sho_shieldeff = TRUE;
3596 break;
3598 tmp = d(nd, 6);
3599 if (!rn2(6))
3600 acid_damage(MON_WEP(mon));
3601 if (!rn2(6))
3602 erode_armor(mon, ERODE_CORRODE);
3603 break;
3605 if (sho_shieldeff)
3606 shieldeff(mon->mx, mon->my);
3607 if (is_hero_spell(type) && (Role_if(PM_KNIGHT) && u.uhave.questart))
3608 tmp *= 2;
3609 if (tmp > 0 && type >= 0
3610 && resist(mon, type < ZT_SPELL(0) ? WAND_CLASS : '\0', 0, NOTELL))
3611 tmp /= 2;
3612 if (tmp < 0)
3613 tmp = 0; /* don't allow negative damage */
3614 debugpline3("zapped monster hp = %d (= %d - %d)", mon->mhp - tmp,
3615 mon->mhp, tmp);
3616 mon->mhp -= tmp;
3617 return tmp;
3620 STATIC_OVL void
3621 zhitu(type, nd, fltxt, sx, sy)
3622 int type, nd;
3623 const char *fltxt;
3624 xchar sx, sy;
3626 int dam = 0, abstyp = abs(type);
3628 switch (abstyp % 10) {
3629 case ZT_MAGIC_MISSILE:
3630 if (Antimagic) {
3631 shieldeff(sx, sy);
3632 pline_The("missiles bounce off!");
3633 } else {
3634 dam = d(nd, 6);
3635 exercise(A_STR, FALSE);
3637 break;
3638 case ZT_FIRE:
3639 if (Fire_resistance) {
3640 shieldeff(sx, sy);
3641 You("don't feel hot!");
3642 ugolemeffects(AD_FIRE, d(nd, 6));
3643 } else {
3644 dam = d(nd, 6);
3646 burn_away_slime();
3647 if (burnarmor(&youmonst)) { /* "body hit" */
3648 if (!rn2(3))
3649 destroy_item(POTION_CLASS, AD_FIRE);
3650 if (!rn2(3))
3651 destroy_item(SCROLL_CLASS, AD_FIRE);
3652 if (!rn2(5))
3653 destroy_item(SPBOOK_CLASS, AD_FIRE);
3654 destroy_item(FOOD_CLASS, AD_FIRE);
3656 break;
3657 case ZT_COLD:
3658 if (Cold_resistance) {
3659 shieldeff(sx, sy);
3660 You("don't feel cold.");
3661 ugolemeffects(AD_COLD, d(nd, 6));
3662 } else {
3663 dam = d(nd, 6);
3665 if (!rn2(3))
3666 destroy_item(POTION_CLASS, AD_COLD);
3667 break;
3668 case ZT_SLEEP:
3669 if (Sleep_resistance) {
3670 shieldeff(u.ux, u.uy);
3671 You("don't feel sleepy.");
3672 } else {
3673 fall_asleep(-d(nd, 25), TRUE); /* sleep ray */
3675 break;
3676 case ZT_DEATH:
3677 if (abstyp == ZT_BREATH(ZT_DEATH)) {
3678 if (Disint_resistance) {
3679 You("are not disintegrated.");
3680 break;
3681 } else if (uarms) {
3682 /* destroy shield; other possessions are safe */
3683 (void) destroy_arm(uarms);
3684 break;
3685 } else if (uarm) {
3686 /* destroy suit; if present, cloak goes too */
3687 if (uarmc)
3688 (void) destroy_arm(uarmc);
3689 (void) destroy_arm(uarm);
3690 break;
3692 /* no shield or suit, you're dead; wipe out cloak
3693 and/or shirt in case of life-saving or bones */
3694 if (uarmc)
3695 (void) destroy_arm(uarmc);
3696 if (uarmu)
3697 (void) destroy_arm(uarmu);
3698 } else if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
3699 shieldeff(sx, sy);
3700 You("seem unaffected.");
3701 break;
3702 } else if (Antimagic) {
3703 shieldeff(sx, sy);
3704 You("aren't affected.");
3705 break;
3707 killer.format = KILLED_BY_AN;
3708 Strcpy(killer.name, fltxt ? fltxt : "");
3709 /* when killed by disintegration breath, don't leave corpse */
3710 u.ugrave_arise = (type == -ZT_BREATH(ZT_DEATH)) ? -3 : NON_PM;
3711 done(DIED);
3712 return; /* lifesaved */
3713 case ZT_LIGHTNING:
3714 if (Shock_resistance) {
3715 shieldeff(sx, sy);
3716 You("aren't affected.");
3717 ugolemeffects(AD_ELEC, d(nd, 6));
3718 } else {
3719 dam = d(nd, 6);
3720 exercise(A_CON, FALSE);
3722 if (!rn2(3))
3723 destroy_item(WAND_CLASS, AD_ELEC);
3724 if (!rn2(3))
3725 destroy_item(RING_CLASS, AD_ELEC);
3726 break;
3727 case ZT_POISON_GAS:
3728 poisoned("blast", A_DEX, "poisoned blast", 15, FALSE);
3729 break;
3730 case ZT_ACID:
3731 if (Acid_resistance) {
3732 pline_The("%s doesn't hurt.", hliquid("acid"));
3733 dam = 0;
3734 } else {
3735 pline_The("%s burns!", hliquid("acid"));
3736 dam = d(nd, 6);
3737 exercise(A_STR, FALSE);
3739 /* using two weapons at once makes both of them more vulnerable */
3740 if (!rn2(u.twoweap ? 3 : 6))
3741 acid_damage(uwep);
3742 if (u.twoweap && !rn2(3))
3743 acid_damage(uswapwep);
3744 if (!rn2(6))
3745 erode_armor(&youmonst, ERODE_CORRODE);
3746 break;
3749 /* Half_spell_damage protection yields half-damage for wands & spells,
3750 including hero's own ricochets; breath attacks do full damage */
3751 if (dam && Half_spell_damage && !(abstyp >= 20 && abstyp <= 29))
3752 dam = (dam + 1) / 2;
3753 losehp(dam, fltxt, KILLED_BY_AN);
3754 return;
3758 * burn objects (such as scrolls and spellbooks) on floor
3759 * at position x,y; return the number of objects burned
3762 burn_floor_objects(x, y, give_feedback, u_caused)
3763 int x, y;
3764 boolean give_feedback; /* caller needs to decide about visibility checks */
3765 boolean u_caused;
3767 struct obj *obj, *obj2;
3768 long i, scrquan, delquan;
3769 char buf1[BUFSZ], buf2[BUFSZ];
3770 int cnt = 0;
3772 for (obj = level.objects[x][y]; obj; obj = obj2) {
3773 obj2 = obj->nexthere;
3774 if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS
3775 || (obj->oclass == FOOD_CLASS
3776 && obj->otyp == GLOB_OF_GREEN_SLIME)) {
3777 if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL
3778 || obj_resists(obj, 2, 100))
3779 continue;
3780 scrquan = obj->quan; /* number present */
3781 delquan = 0L; /* number to destroy */
3782 for (i = scrquan; i > 0L; i--)
3783 if (!rn2(3))
3784 delquan++;
3785 if (delquan) {
3786 /* save name before potential delobj() */
3787 if (give_feedback) {
3788 obj->quan = 1L;
3789 Strcpy(buf1, (x == u.ux && y == u.uy)
3790 ? xname(obj)
3791 : distant_name(obj, xname));
3792 obj->quan = 2L;
3793 Strcpy(buf2, (x == u.ux && y == u.uy)
3794 ? xname(obj)
3795 : distant_name(obj, xname));
3796 obj->quan = scrquan;
3798 /* useupf(), which charges, only if hero caused damage */
3799 if (u_caused)
3800 useupf(obj, delquan);
3801 else if (delquan < scrquan)
3802 obj->quan -= delquan;
3803 else
3804 delobj(obj);
3805 cnt += delquan;
3806 if (give_feedback) {
3807 if (delquan > 1L)
3808 pline("%ld %s burn.", delquan, buf2);
3809 else
3810 pline("%s burns.", An(buf1));
3815 return cnt;
3818 /* will zap/spell/breath attack score a hit against armor class `ac'? */
3819 STATIC_OVL int
3820 zap_hit(ac, type)
3821 int ac;
3822 int type; /* either hero cast spell type or 0 */
3824 int chance = rn2(20);
3825 int spell_bonus = type ? spell_hit_bonus(type) : 0;
3827 /* small chance for naked target to avoid being hit */
3828 if (!chance)
3829 return rnd(10) < ac + spell_bonus;
3831 /* very high armor protection does not achieve invulnerability */
3832 ac = AC_VALUE(ac);
3834 return (3 - chance < ac + spell_bonus);
3837 STATIC_OVL void
3838 disintegrate_mon(mon, type, fltxt)
3839 struct monst *mon;
3840 int type; /* hero vs other */
3841 const char *fltxt;
3843 struct obj *otmp, *otmp2, *m_amulet = mlifesaver(mon);
3845 if (canseemon(mon)) {
3846 if (!m_amulet)
3847 pline("%s is disintegrated!", Monnam(mon));
3848 else
3849 hit(fltxt, mon, "!");
3852 /* note: worn amulet of life saving must be preserved in order to operate */
3853 #define oresist_disintegration(obj) \
3854 (objects[obj->otyp].oc_oprop == DISINT_RES || obj_resists(obj, 5, 50) \
3855 || is_quest_artifact(obj) || obj == m_amulet)
3857 for (otmp = mon->minvent; otmp; otmp = otmp2) {
3858 otmp2 = otmp->nobj;
3859 if (!oresist_disintegration(otmp)) {
3860 if (otmp->owornmask) {
3861 /* in case monster's life gets saved */
3862 mon->misc_worn_check &= ~otmp->owornmask;
3863 if (otmp->owornmask & W_WEP)
3864 setmnotwielded(mon, otmp);
3865 /* also dismounts hero if this object is steed's saddle */
3866 update_mon_intrinsics(mon, otmp, FALSE, TRUE);
3867 otmp->owornmask = 0L;
3869 obj_extract_self(otmp);
3870 obfree(otmp, (struct obj *) 0);
3874 #undef oresist_disintegration
3876 if (type < 0)
3877 monkilled(mon, (char *) 0, -AD_RBRE);
3878 else
3879 xkilled(mon, XKILL_NOMSG | XKILL_NOCORPSE);
3882 void
3883 buzz(type,nd,sx,sy,dx,dy)
3884 int type, nd;
3885 xchar sx,sy;
3886 int dx,dy;
3888 dobuzz(type, nd, sx, sy, dx, dy, TRUE);
3892 * type == 0 to 9 : you shooting a wand
3893 * type == 10 to 19 : you casting a spell
3894 * type == 20 to 29 : you breathing as a monster
3895 * type == -10 to -19 : monster casting spell
3896 * type == -20 to -29 : monster breathing at you
3897 * type == -30 to -39 : monster shooting a wand
3898 * called with dx = dy = 0 with vertical bolts
3900 void
3901 dobuzz(type, nd, sx, sy, dx, dy,say)
3902 register int type, nd;
3903 register xchar sx, sy;
3904 register int dx, dy;
3905 boolean say; /* Announce out of sight hit/miss events if true */
3907 int range, abstype = abs(type) % 10;
3908 register xchar lsx, lsy;
3909 struct monst *mon;
3910 coord save_bhitpos;
3911 boolean shopdamage = FALSE;
3912 const char *fltxt;
3913 struct obj *otmp;
3914 int spell_type;
3916 /* if its a Hero Spell then get its SPE_TYPE */
3917 spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + abstype : 0;
3919 fltxt = flash_types[(type <= -30) ? abstype : abs(type)];
3920 if (u.uswallow) {
3921 register int tmp;
3923 if (type < 0)
3924 return;
3925 tmp = zhitm(u.ustuck, type, nd, &otmp);
3926 if (!u.ustuck)
3927 u.uswallow = 0;
3928 else
3929 pline("%s rips into %s%s", The(fltxt), mon_nam(u.ustuck),
3930 exclam(tmp));
3931 /* Using disintegration from the inside only makes a hole... */
3932 if (tmp == MAGIC_COOKIE)
3933 u.ustuck->mhp = 0;
3934 if (u.ustuck->mhp < 1)
3935 killed(u.ustuck);
3936 return;
3938 if (type < 0)
3939 newsym(u.ux, u.uy);
3940 range = rn1(7, 7);
3941 if (dx == 0 && dy == 0)
3942 range = 1;
3943 save_bhitpos = bhitpos;
3945 tmp_at(DISP_BEAM, zapdir_to_glyph(dx, dy, abstype));
3946 while (range-- > 0) {
3947 lsx = sx;
3948 sx += dx;
3949 lsy = sy;
3950 sy += dy;
3951 if (!isok(sx, sy) || levl[sx][sy].typ == STONE)
3952 goto make_bounce;
3954 mon = m_at(sx, sy);
3955 if (cansee(sx, sy)) {
3956 /* reveal/unreveal invisible monsters before tmp_at() */
3957 if (mon && !canspotmon(mon))
3958 map_invisible(sx, sy);
3959 else if (!mon && glyph_is_invisible(levl[sx][sy].glyph)) {
3960 unmap_object(sx, sy);
3961 newsym(sx, sy);
3963 if (ZAP_POS(levl[sx][sy].typ)
3964 || (isok(lsx, lsy) && cansee(lsx, lsy)))
3965 tmp_at(sx, sy);
3966 delay_output(); /* wait a little */
3969 /* hit() and miss() need bhitpos to match the target */
3970 bhitpos.x = sx, bhitpos.y = sy;
3971 /* Fireballs only damage when they explode */
3972 if (type != ZT_SPELL(ZT_FIRE))
3973 range += zap_over_floor(sx, sy, type, &shopdamage, 0);
3975 if (mon) {
3976 if (type == ZT_SPELL(ZT_FIRE))
3977 break;
3978 if (type >= 0)
3979 mon->mstrategy &= ~STRAT_WAITMASK;
3980 buzzmonst:
3981 notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y);
3982 if (zap_hit(find_mac(mon), spell_type)) {
3983 if (mon_reflects(mon, (char *) 0)) {
3984 if (cansee(mon->mx, mon->my)) {
3985 hit(fltxt, mon, exclam(0));
3986 shieldeff(mon->mx, mon->my);
3987 (void) mon_reflects(mon,
3988 "But it reflects from %s %s!");
3990 dx = -dx;
3991 dy = -dy;
3992 } else {
3993 boolean mon_could_move = mon->mcanmove;
3994 int tmp = zhitm(mon, type, nd, &otmp);
3996 if (is_rider(mon->data)
3997 && abs(type) == ZT_BREATH(ZT_DEATH)) {
3998 if (canseemon(mon)) {
3999 hit(fltxt, mon, ".");
4000 pline("%s disintegrates.", Monnam(mon));
4001 pline("%s body reintegrates before your %s!",
4002 s_suffix(Monnam(mon)),
4003 (eyecount(youmonst.data) == 1)
4004 ? body_part(EYE)
4005 : makeplural(body_part(EYE)));
4006 pline("%s resurrects!", Monnam(mon));
4008 mon->mhp = mon->mhpmax;
4009 break; /* Out of while loop */
4011 if (mon->data == &mons[PM_DEATH] && abstype == ZT_DEATH) {
4012 if (canseemon(mon)) {
4013 hit(fltxt, mon, ".");
4014 pline("%s absorbs the deadly %s!", Monnam(mon),
4015 type == ZT_BREATH(ZT_DEATH) ? "blast"
4016 : "ray");
4017 pline("It seems even stronger than before.");
4019 break; /* Out of while loop */
4022 if (tmp == MAGIC_COOKIE) { /* disintegration */
4023 disintegrate_mon(mon, type, fltxt);
4024 } else if (mon->mhp < 1) {
4025 if (type < 0)
4026 monkilled(mon, fltxt, AD_RBRE);
4027 else
4028 killed(mon);
4029 } else {
4030 if (!otmp) {
4031 /* normal non-fatal hit */
4032 if (say || canseemon(mon))
4033 hit(fltxt, mon, exclam(tmp));
4034 } else {
4035 /* some armor was destroyed; no damage done */
4036 if (canseemon(mon))
4037 pline("%s %s is disintegrated!",
4038 s_suffix(Monnam(mon)),
4039 distant_name(otmp, xname));
4040 m_useup(mon, otmp);
4042 if (mon_could_move && !mon->mcanmove) /* ZT_SLEEP */
4043 slept_monst(mon);
4046 range -= 2;
4047 } else {
4048 if (say || canseemon(mon))
4049 miss(fltxt, mon);
4051 } else if (sx == u.ux && sy == u.uy && range >= 0) {
4052 nomul(0);
4053 if (u.usteed && !rn2(3) && !mon_reflects(u.usteed, (char *) 0)) {
4054 mon = u.usteed;
4055 goto buzzmonst;
4056 } else if (zap_hit((int) u.uac, 0)) {
4057 range -= 2;
4058 pline("%s hits you!", The(fltxt));
4059 if (Reflecting) {
4060 if (!Blind) {
4061 (void) ureflects("But %s reflects from your %s!",
4062 "it");
4063 } else
4064 pline("For some reason you are not affected.");
4065 dx = -dx;
4066 dy = -dy;
4067 shieldeff(sx, sy);
4068 } else {
4069 zhitu(type, nd, fltxt, sx, sy);
4071 } else if (!Blind) {
4072 pline("%s whizzes by you!", The(fltxt));
4073 } else if (abstype == ZT_LIGHTNING) {
4074 Your("%s tingles.", body_part(ARM));
4076 if (abstype == ZT_LIGHTNING)
4077 (void) flashburn((long) d(nd, 50));
4078 stop_occupation();
4079 nomul(0);
4082 if (!ZAP_POS(levl[sx][sy].typ)
4083 || (closed_door(sx, sy) && range >= 0)) {
4084 int bounce, bchance;
4085 uchar rmn;
4086 boolean fireball;
4088 make_bounce:
4089 bchance = (levl[sx][sy].typ == STONE) ? 10
4090 : (In_mines(&u.uz) && IS_WALL(levl[sx][sy].typ)) ? 20
4091 : 75;
4092 bounce = 0;
4093 fireball = (type == ZT_SPELL(ZT_FIRE));
4094 if ((--range > 0 && isok(lsx, lsy) && cansee(lsx, lsy))
4095 || fireball) {
4096 if (Is_airlevel(&u.uz)) { /* nothing to bounce off of */
4097 pline_The("%s vanishes into the aether!", fltxt);
4098 if (fireball)
4099 type = ZT_WAND(ZT_FIRE); /* skip pending fireball */
4100 break;
4101 } else if (fireball) {
4102 sx = lsx;
4103 sy = lsy;
4104 break; /* fireballs explode before the obstacle */
4105 } else
4106 pline_The("%s bounces!", fltxt);
4108 if (!dx || !dy || !rn2(bchance)) {
4109 dx = -dx;
4110 dy = -dy;
4111 } else {
4112 if (isok(sx, lsy) && ZAP_POS(rmn = levl[sx][lsy].typ)
4113 && !closed_door(sx, lsy)
4114 && (IS_ROOM(rmn) || (isok(sx + dx, lsy)
4115 && ZAP_POS(levl[sx + dx][lsy].typ))))
4116 bounce = 1;
4117 if (isok(lsx, sy) && ZAP_POS(rmn = levl[lsx][sy].typ)
4118 && !closed_door(lsx, sy)
4119 && (IS_ROOM(rmn) || (isok(lsx, sy + dy)
4120 && ZAP_POS(levl[lsx][sy + dy].typ))))
4121 if (!bounce || rn2(2))
4122 bounce = 2;
4124 switch (bounce) {
4125 case 0:
4126 dx = -dx; /* fall into... */
4127 case 1:
4128 dy = -dy;
4129 break;
4130 case 2:
4131 dx = -dx;
4132 break;
4134 tmp_at(DISP_CHANGE, zapdir_to_glyph(dx, dy, abstype));
4138 tmp_at(DISP_END, 0);
4139 if (type == ZT_SPELL(ZT_FIRE))
4140 explode(sx, sy, type, d(12, 6), 0, EXPL_FIERY);
4141 if (shopdamage)
4142 pay_for_damage(abstype == ZT_FIRE
4143 ? "burn away"
4144 : abstype == ZT_COLD
4145 ? "shatter"
4146 /* "damage" indicates wall rather than door */
4147 : abstype == ZT_ACID
4148 ? "damage"
4149 : abstype == ZT_DEATH
4150 ? "disintegrate"
4151 : "destroy",
4152 FALSE);
4153 bhitpos = save_bhitpos;
4156 void
4157 melt_ice(x, y, msg)
4158 xchar x, y;
4159 const char *msg;
4161 struct rm *lev = &levl[x][y];
4162 struct obj *otmp;
4164 if (!msg)
4165 msg = "The ice crackles and melts.";
4166 if (lev->typ == DRAWBRIDGE_UP) {
4167 lev->drawbridgemask &= ~DB_ICE; /* revert to DB_MOAT */
4168 } else { /* lev->typ == ICE */
4169 #ifdef STUPID
4170 if (lev->icedpool == ICED_POOL)
4171 lev->typ = POOL;
4172 else
4173 lev->typ = MOAT;
4174 #else
4175 lev->typ = (lev->icedpool == ICED_POOL ? POOL : MOAT);
4176 #endif
4177 lev->icedpool = 0;
4179 spot_stop_timers(x, y, MELT_ICE_AWAY); /* no more ice to melt away */
4180 obj_ice_effects(x, y, FALSE);
4181 unearth_objs(x, y);
4182 if (Underwater)
4183 vision_recalc(1);
4184 newsym(x, y);
4185 if (cansee(x, y))
4186 Norep("%s", msg);
4187 if ((otmp = sobj_at(BOULDER, x, y)) != 0) {
4188 if (cansee(x, y))
4189 pline("%s settles...", An(xname(otmp)));
4190 do {
4191 obj_extract_self(otmp); /* boulder isn't being pushed */
4192 if (!boulder_hits_pool(otmp, x, y, FALSE))
4193 impossible("melt_ice: no pool?");
4194 /* try again if there's another boulder and pool didn't fill */
4195 } while (is_pool(x, y) && (otmp = sobj_at(BOULDER, x, y)) != 0);
4196 newsym(x, y);
4198 if (x == u.ux && y == u.uy)
4199 spoteffects(TRUE); /* possibly drown, notice objects */
4202 #define MIN_ICE_TIME 50
4203 #define MAX_ICE_TIME 2000
4205 * Usually start a melt_ice timer; sometimes the ice will become
4206 * permanent instead.
4208 void
4209 start_melt_ice_timeout(x, y, min_time)
4210 xchar x, y;
4211 long min_time; /* <x,y>'s old melt timeout (deleted by time we get here) */
4213 int when;
4214 long where;
4216 when = (int) min_time;
4217 if (when < MIN_ICE_TIME - 1)
4218 when = MIN_ICE_TIME - 1;
4220 /* random timeout; surrounding ice locations ought to be a factor... */
4221 while (++when <= MAX_ICE_TIME)
4222 if (!rn2((MAX_ICE_TIME - when) + MIN_ICE_TIME))
4223 break;
4225 /* if we're within MAX_ICE_TIME, install a melt timer;
4226 otherwise, omit it to leave this ice permanent */
4227 if (when <= MAX_ICE_TIME) {
4228 where = ((long) x << 16) | (long) y;
4229 (void) start_timer((long) when, TIMER_LEVEL, MELT_ICE_AWAY,
4230 long_to_any(where));
4233 #undef MIN_ICE_TIME
4234 #undef MAX_ICE_TIME
4237 * Called when ice has melted completely away.
4239 void
4240 melt_ice_away(arg, timeout)
4241 anything *arg;
4242 long timeout UNUSED;
4244 xchar x, y;
4245 long where = arg->a_long;
4247 y = (xchar) (where & 0xFFFF);
4248 x = (xchar) ((where >> 16) & 0xFFFF);
4249 /* melt_ice does newsym when appropriate */
4250 melt_ice(x, y, "Some ice melts away.");
4253 /* Burn floor scrolls, evaporate pools, etc... in a single square.
4254 * Used both for normal bolts of fire, cold, etc... and for fireballs.
4255 * Sets shopdamage to TRUE if a shop door is destroyed, and returns the
4256 * amount by which range is reduced (the latter is just ignored by fireballs)
4259 zap_over_floor(x, y, type, shopdamage, exploding_wand_typ)
4260 xchar x, y;
4261 int type;
4262 boolean *shopdamage;
4263 short exploding_wand_typ;
4265 const char *zapverb;
4266 struct monst *mon;
4267 struct trap *t;
4268 struct rm *lev = &levl[x][y];
4269 boolean see_it = cansee(x, y), yourzap;
4270 int rangemod = 0, abstype = abs(type) % 10;
4272 switch (abstype) {
4273 case ZT_FIRE:
4274 t = t_at(x, y);
4275 if (t && t->ttyp == WEB) {
4276 /* a burning web is too flimsy to notice if you can't see it */
4277 if (see_it)
4278 Norep("A web bursts into flames!");
4279 (void) delfloortrap(t);
4280 if (see_it)
4281 newsym(x, y);
4283 if (is_ice(x, y)) {
4284 melt_ice(x, y, (char *) 0);
4285 } else if (is_pool(x, y)) {
4286 const char *msgtxt = "You hear hissing gas.";
4288 if (lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP */
4289 if (see_it)
4290 msgtxt = "Some water evaporates.";
4291 } else {
4292 rangemod -= 3;
4293 lev->typ = ROOM;
4294 t = maketrap(x, y, PIT);
4295 if (t)
4296 t->tseen = 1;
4297 if (see_it)
4298 msgtxt = "The water evaporates.";
4300 Norep("%s", msgtxt);
4301 if (lev->typ == ROOM)
4302 newsym(x, y);
4303 } else if (IS_FOUNTAIN(lev->typ)) {
4304 if (see_it)
4305 pline("Steam billows from the fountain.");
4306 rangemod -= 1;
4307 dryup(x, y, type > 0);
4309 break; /* ZT_FIRE */
4311 case ZT_COLD:
4312 if (is_pool(x, y) || is_lava(x, y)) {
4313 boolean lava = is_lava(x, y),
4314 moat = is_moat(x, y);
4316 if (lev->typ == WATER) {
4317 /* For now, don't let WATER freeze. */
4318 if (see_it)
4319 pline_The("%s freezes for a moment.", hliquid("water"));
4320 else
4321 You_hear("a soft crackling.");
4322 rangemod -= 1000; /* stop */
4323 } else {
4324 char buf[BUFSZ];
4326 Strcpy(buf, waterbody_name(x, y)); /* for MOAT */
4327 rangemod -= 3;
4328 if (lev->typ == DRAWBRIDGE_UP) {
4329 lev->drawbridgemask &= ~DB_UNDER; /* clear lava */
4330 lev->drawbridgemask |= (lava ? DB_FLOOR : DB_ICE);
4331 } else {
4332 if (!lava)
4333 lev->icedpool = (lev->typ == POOL) ? ICED_POOL
4334 : ICED_MOAT;
4335 lev->typ = lava ? ROOM : ICE;
4337 bury_objs(x, y);
4338 if (see_it) {
4339 if (lava)
4340 Norep("The %s cools and solidifies.", hliquid("lava"));
4341 else if (moat)
4342 Norep("The %s is bridged with ice!", buf);
4343 else
4344 Norep("The %s freezes.", hliquid("water"));
4345 newsym(x, y);
4346 } else if (!lava)
4347 You_hear("a crackling sound.");
4349 if (x == u.ux && y == u.uy) {
4350 if (u.uinwater) { /* not just `if (Underwater)' */
4351 /* leave the no longer existent water */
4352 u.uinwater = 0;
4353 u.uundetected = 0;
4354 docrt();
4355 vision_full_recalc = 1;
4356 } else if (u.utrap && u.utraptype == TT_LAVA) {
4357 if (Passes_walls) {
4358 u.utrap = 0;
4359 You("pass through the now-solid rock.");
4360 } else {
4361 u.utrap = rn1(50, 20);
4362 u.utraptype = TT_INFLOOR;
4363 You("are firmly stuck in the cooling rock.");
4366 } else if ((mon = m_at(x, y)) != 0) {
4367 /* probably ought to do some hefty damage to any
4368 non-ice creature caught in freezing water;
4369 at a minimum, eels are forced out of hiding */
4370 if (is_swimmer(mon->data) && mon->mundetected) {
4371 mon->mundetected = 0;
4372 newsym(x, y);
4375 if (!lava) {
4376 start_melt_ice_timeout(x, y, 0L);
4377 obj_ice_effects(x, y, TRUE);
4379 } /* ?WATER */
4381 } else if (is_ice(x, y)) {
4382 long melt_time;
4384 /* Already ice here, so just firm it up. */
4385 /* Now ensure that only ice that is already timed is affected */
4386 if ((melt_time = spot_time_left(x, y, MELT_ICE_AWAY)) != 0L) {
4387 spot_stop_timers(x, y, MELT_ICE_AWAY);
4388 start_melt_ice_timeout(x, y, melt_time);
4391 break; /* ZT_COLD */
4393 case ZT_POISON_GAS:
4394 (void) create_gas_cloud(x, y, 1, 8);
4395 break;
4397 case ZT_ACID:
4398 if (lev->typ == IRONBARS) {
4399 if ((lev->wall_info & W_NONDIGGABLE) != 0) {
4400 if (see_it)
4401 Norep("The %s corrode somewhat but remain intact.",
4402 defsyms[S_bars].explanation);
4403 /* but nothing actually happens... */
4404 } else {
4405 rangemod -= 3;
4406 if (see_it)
4407 Norep("The %s melt.", defsyms[S_bars].explanation);
4408 if (*in_rooms(x, y, SHOPBASE)) {
4409 /* in case we ever have a shop bounded by bars */
4410 lev->typ = ROOM;
4411 if (see_it)
4412 newsym(x, y);
4413 add_damage(x, y, (type >= 0) ? 300L : 0L);
4414 if (type >= 0)
4415 *shopdamage = TRUE;
4416 } else {
4417 lev->typ = DOOR;
4418 lev->doormask = D_NODOOR;
4419 if (see_it)
4420 newsym(x, y);
4424 break; /* ZT_ACID */
4426 default:
4427 break;
4430 /* set up zap text for possible door feedback; for exploding wand, we
4431 want "the blast" rather than "your blast" even if hero caused it */
4432 yourzap = (type >= 0 && !exploding_wand_typ);
4433 zapverb = "blast"; /* breath attack or wand explosion */
4434 if (!exploding_wand_typ) {
4435 if (abs(type) < ZT_SPELL(0))
4436 zapverb = "bolt"; /* wand zap */
4437 else if (abs(type) < ZT_BREATH(0))
4438 zapverb = "spell";
4441 /* secret door gets revealed, converted into regular door */
4442 if (levl[x][y].typ == SDOOR) {
4443 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
4444 /* target spot will now pass closed_door() test below
4445 (except on rogue level) */
4446 newsym(x, y);
4447 if (see_it)
4448 pline("%s %s reveals a secret door.",
4449 yourzap ? "Your" : "The", zapverb);
4450 else if (Is_rogue_level(&u.uz))
4451 draft_message(FALSE); /* "You feel a draft." (open doorway) */
4454 /* regular door absorbs remaining zap range, possibly gets destroyed */
4455 if (closed_door(x, y)) {
4456 int new_doormask = -1;
4457 const char *see_txt = 0, *sense_txt = 0, *hear_txt = 0;
4459 rangemod = -1000;
4460 switch (abstype) {
4461 case ZT_FIRE:
4462 new_doormask = D_NODOOR;
4463 see_txt = "The door is consumed in flames!";
4464 sense_txt = "smell smoke.";
4465 break;
4466 case ZT_COLD:
4467 new_doormask = D_NODOOR;
4468 see_txt = "The door freezes and shatters!";
4469 sense_txt = "feel cold.";
4470 break;
4471 case ZT_DEATH:
4472 /* death spells/wands don't disintegrate */
4473 if (abs(type) != ZT_BREATH(ZT_DEATH))
4474 goto def_case;
4475 new_doormask = D_NODOOR;
4476 see_txt = "The door disintegrates!";
4477 hear_txt = "crashing wood.";
4478 break;
4479 case ZT_LIGHTNING:
4480 new_doormask = D_BROKEN;
4481 see_txt = "The door splinters!";
4482 hear_txt = "crackling.";
4483 break;
4484 default:
4485 def_case:
4486 if (exploding_wand_typ > 0) {
4487 /* Magical explosion from misc exploding wand */
4488 if (exploding_wand_typ == WAN_STRIKING) {
4489 new_doormask = D_BROKEN;
4490 see_txt = "The door crashes open!";
4491 sense_txt = "feel a burst of cool air.";
4492 break;
4495 if (see_it) {
4496 /* "the door absorbs the blast" would be
4497 inaccurate for an exploding wand since
4498 other adjacent locations still get hit */
4499 if (exploding_wand_typ)
4500 pline_The("door remains intact.");
4501 else
4502 pline_The("door absorbs %s %s!", yourzap ? "your" : "the",
4503 zapverb);
4504 } else
4505 You_feel("vibrations.");
4506 break;
4508 if (new_doormask >= 0) { /* door gets broken */
4509 if (*in_rooms(x, y, SHOPBASE)) {
4510 if (type >= 0) {
4511 add_damage(x, y, 400L);
4512 *shopdamage = TRUE;
4513 } else /* caused by monster */
4514 add_damage(x, y, 0L);
4516 lev->doormask = new_doormask;
4517 unblock_point(x, y); /* vision */
4518 if (see_it) {
4519 pline1(see_txt);
4520 newsym(x, y);
4521 } else if (sense_txt) {
4522 You1(sense_txt);
4523 } else if (hear_txt)
4524 You_hear1(hear_txt);
4525 if (picking_at(x, y)) {
4526 stop_occupation();
4527 reset_pick();
4532 if (OBJ_AT(x, y) && abstype == ZT_FIRE)
4533 if (burn_floor_objects(x, y, FALSE, type > 0) && couldsee(x, y)) {
4534 newsym(x, y);
4535 You("%s of smoke.", !Blind ? "see a puff" : "smell a whiff");
4537 if ((mon = m_at(x, y)) != 0) {
4538 wakeup(mon, FALSE);
4539 if (type >= 0) {
4540 setmangry(mon, TRUE);
4541 if (mon->ispriest && *in_rooms(mon->mx, mon->my, TEMPLE))
4542 ghod_hitsu(mon);
4543 if (mon->isshk && !*u.ushops)
4544 hot_pursuit(mon);
4547 return rangemod;
4550 /* fractured by pick-axe or wand of striking */
4551 void
4552 fracture_rock(obj)
4553 register struct obj *obj; /* no texts here! */
4555 xchar x, y;
4556 boolean by_you = !context.mon_moving;
4558 if (by_you && get_obj_location(obj, &x, &y, 0) && costly_spot(x, y)) {
4559 struct monst *shkp = 0;
4560 char objroom = *in_rooms(x, y, SHOPBASE);
4562 if (billable(&shkp, obj, objroom, FALSE)) {
4563 /* shop message says "you owe <shk> <$> for it!" so we need
4564 to precede that with a message explaining what "it" is */
4565 You("fracture %s %s.", s_suffix(shkname(shkp)), xname(obj));
4566 breakobj(obj, x, y, TRUE, FALSE); /* charges for shop goods */
4569 if (by_you && obj->otyp == BOULDER)
4570 sokoban_guilt();
4572 obj->otyp = ROCK;
4573 obj->oclass = GEM_CLASS;
4574 obj->quan = (long) rn1(60, 7);
4575 obj->owt = weight(obj);
4576 obj->dknown = obj->bknown = obj->rknown = 0;
4577 obj->known = objects[obj->otyp].oc_uses_known ? 0 : 1;
4578 dealloc_oextra(obj);
4580 if (obj->where == OBJ_FLOOR) {
4581 obj_extract_self(obj); /* move rocks back on top */
4582 place_object(obj, obj->ox, obj->oy);
4583 if (!does_block(obj->ox, obj->oy, &levl[obj->ox][obj->oy]))
4584 unblock_point(obj->ox, obj->oy);
4585 if (cansee(obj->ox, obj->oy))
4586 newsym(obj->ox, obj->oy);
4590 /* handle statue hit by striking/force bolt/pick-axe */
4591 boolean
4592 break_statue(obj)
4593 register struct obj *obj;
4595 /* [obj is assumed to be on floor, so no get_obj_location() needed] */
4596 struct trap *trap = t_at(obj->ox, obj->oy);
4597 struct obj *item;
4598 boolean by_you = !context.mon_moving;
4600 if (trap && trap->ttyp == STATUE_TRAP
4601 && activate_statue_trap(trap, obj->ox, obj->oy, TRUE))
4602 return FALSE;
4603 /* drop any objects contained inside the statue */
4604 while ((item = obj->cobj) != 0) {
4605 obj_extract_self(item);
4606 place_object(item, obj->ox, obj->oy);
4608 if (by_you && Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC)) {
4609 You_feel("guilty about damaging such a historic statue.");
4610 adjalign(-1);
4612 obj->spe = 0;
4613 fracture_rock(obj);
4614 return TRUE;
4618 * destroy_strings[dindx][0:singular,1:plural,2:killer_reason]
4619 * [0] freezing potion
4620 * [1] boiling potion other than oil
4621 * [2] boiling potion of oil
4622 * [3] burning scroll
4623 * [4] burning spellbook
4624 * [5] shocked ring
4625 * [6] shocked wand
4626 * (books, rings, and wands don't stack so don't need plural form;
4627 * crumbling ring doesn't do damage so doesn't need killer reason)
4629 const char *const destroy_strings[][3] = {
4630 /* also used in trap.c */
4631 { "freezes and shatters", "freeze and shatter", "shattered potion" },
4632 { "boils and explodes", "boil and explode", "boiling potion" },
4633 { "ignites and explodes", "ignite and explode", "exploding potion" },
4634 { "catches fire and burns", "catch fire and burn", "burning scroll" },
4635 { "catches fire and burns", "", "burning book" },
4636 { "turns to dust and vanishes", "", "" },
4637 { "breaks apart and explodes", "", "exploding wand" },
4640 void
4641 destroy_item(osym, dmgtyp)
4642 register int osym, dmgtyp;
4644 register struct obj *obj, *obj2;
4645 int dmg, xresist, skip;
4646 long i, cnt, quan;
4647 int dindx;
4648 const char *mult;
4649 boolean physical_damage;
4651 for (obj = invent; obj; obj = obj2) {
4652 obj2 = obj->nobj;
4653 physical_damage = FALSE;
4654 if (obj->oclass != osym)
4655 continue; /* test only objs of type osym */
4656 if (obj->oartifact)
4657 continue; /* don't destroy artifacts */
4658 if (obj->in_use && obj->quan == 1L)
4659 continue; /* not available */
4660 xresist = skip = 0;
4661 /* lint suppression */
4662 dmg = dindx = 0;
4663 quan = 0L;
4665 switch (dmgtyp) {
4666 case AD_COLD:
4667 if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
4668 quan = obj->quan;
4669 dindx = 0;
4670 dmg = rnd(4);
4671 } else
4672 skip++;
4673 break;
4674 case AD_FIRE:
4675 xresist = (Fire_resistance && obj->oclass != POTION_CLASS
4676 && obj->otyp != GLOB_OF_GREEN_SLIME);
4678 if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
4679 skip++;
4680 if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
4681 skip++;
4682 if (!Blind)
4683 pline("%s glows a strange %s, but remains intact.",
4684 The(xname(obj)), hcolor("dark red"));
4686 quan = obj->quan;
4687 switch (osym) {
4688 case POTION_CLASS:
4689 dindx = (obj->otyp != POT_OIL) ? 1 : 2;
4690 dmg = rnd(6);
4691 break;
4692 case SCROLL_CLASS:
4693 dindx = 3;
4694 dmg = 1;
4695 break;
4696 case SPBOOK_CLASS:
4697 dindx = 4;
4698 dmg = 1;
4699 break;
4700 case FOOD_CLASS:
4701 if (obj->otyp == GLOB_OF_GREEN_SLIME) {
4702 dindx = 1; /* boil and explode */
4703 dmg = (obj->owt + 19) / 20;
4704 } else {
4705 skip++;
4707 break;
4708 default:
4709 skip++;
4710 break;
4712 break;
4713 case AD_ELEC:
4714 xresist = (Shock_resistance && obj->oclass != RING_CLASS);
4715 quan = obj->quan;
4716 switch (osym) {
4717 case RING_CLASS:
4718 if (obj->otyp == RIN_SHOCK_RESISTANCE) {
4719 skip++;
4720 break;
4722 dindx = 5;
4723 dmg = 0;
4724 break;
4725 case WAND_CLASS:
4726 if (obj->otyp == WAN_LIGHTNING) {
4727 skip++;
4728 break;
4730 #if 0
4731 if (obj == current_wand) { skip++; break; }
4732 #endif
4733 dindx = 6;
4734 dmg = rnd(10);
4735 break;
4736 default:
4737 skip++;
4738 break;
4740 break;
4741 default:
4742 skip++;
4743 break;
4745 if (!skip) {
4746 if (obj->in_use)
4747 --quan; /* one will be used up elsewhere */
4748 for (i = cnt = 0L; i < quan; i++)
4749 if (!rn2(3))
4750 cnt++;
4752 if (!cnt)
4753 continue;
4754 mult = (cnt == 1L)
4755 ? (quan == 1L) ? "Your" /* 1 of 1 */
4756 : "One of your" /* 1 of N */
4757 : (cnt < quan) ? "Some of your" /* n of N */
4758 : (quan == 2L) ? "Both of your" /* 2 of 2 */
4759 : "All of your"; /* N of N */
4760 pline("%s %s %s!", mult, xname(obj),
4761 destroy_strings[dindx][(cnt > 1L)]);
4762 if (osym == POTION_CLASS && dmgtyp != AD_COLD) {
4763 if (!breathless(youmonst.data) || haseyes(youmonst.data))
4764 potionbreathe(obj);
4766 if (obj->owornmask) {
4767 if (obj->owornmask & W_RING) /* ring being worn */
4768 Ring_gone(obj);
4769 else
4770 setnotworn(obj);
4772 if (obj == current_wand)
4773 current_wand = 0; /* destroyed */
4774 for (i = 0; i < cnt; i++)
4775 useup(obj);
4776 if (dmg) {
4777 if (xresist)
4778 You("aren't hurt!");
4779 else {
4780 const char *how = destroy_strings[dindx][2];
4781 boolean one = (cnt == 1L);
4783 if (dmgtyp == AD_FIRE && osym == FOOD_CLASS)
4784 how = "exploding glob of slime";
4785 if (physical_damage)
4786 dmg = Maybe_Half_Phys(dmg);
4787 losehp(dmg, one ? how : (const char *) makeplural(how),
4788 one ? KILLED_BY_AN : KILLED_BY);
4789 exercise(A_STR, FALSE);
4794 return;
4798 destroy_mitem(mtmp, osym, dmgtyp)
4799 struct monst *mtmp;
4800 int osym, dmgtyp;
4802 struct obj *obj, *obj2;
4803 int skip, tmp = 0;
4804 long i, cnt, quan;
4805 int dindx;
4806 boolean vis;
4808 if (mtmp == &youmonst) { /* this simplifies artifact_hit() */
4809 destroy_item(osym, dmgtyp);
4810 return 0; /* arbitrary; value doesn't matter to artifact_hit() */
4813 vis = canseemon(mtmp);
4814 for (obj = mtmp->minvent; obj; obj = obj2) {
4815 obj2 = obj->nobj;
4816 if (obj->oclass != osym)
4817 continue; /* test only objs of type osym */
4818 skip = 0;
4819 quan = 0L;
4820 dindx = 0;
4822 switch (dmgtyp) {
4823 case AD_COLD:
4824 if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
4825 quan = obj->quan;
4826 dindx = 0;
4827 tmp++;
4828 } else
4829 skip++;
4830 break;
4831 case AD_FIRE:
4832 if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
4833 skip++;
4834 if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
4835 skip++;
4836 if (vis)
4837 pline("%s glows a strange %s, but remains intact.",
4838 The(distant_name(obj, xname)), hcolor("dark red"));
4840 quan = obj->quan;
4841 switch (osym) {
4842 case POTION_CLASS:
4843 dindx = (obj->otyp != POT_OIL) ? 1 : 2;
4844 tmp++;
4845 break;
4846 case SCROLL_CLASS:
4847 dindx = 3;
4848 tmp++;
4849 break;
4850 case SPBOOK_CLASS:
4851 dindx = 4;
4852 tmp++;
4853 break;
4854 case FOOD_CLASS:
4855 if (obj->otyp == GLOB_OF_GREEN_SLIME) {
4856 dindx = 1; /* boil and explode */
4857 tmp += (obj->owt + 19) / 20;
4858 } else {
4859 skip++;
4861 break;
4862 default:
4863 skip++;
4864 break;
4866 break;
4867 case AD_ELEC:
4868 quan = obj->quan;
4869 switch (osym) {
4870 case RING_CLASS:
4871 if (obj->otyp == RIN_SHOCK_RESISTANCE) {
4872 skip++;
4873 break;
4875 dindx = 5;
4876 break;
4877 case WAND_CLASS:
4878 if (obj->otyp == WAN_LIGHTNING) {
4879 skip++;
4880 break;
4882 dindx = 6;
4883 tmp++;
4884 break;
4885 default:
4886 skip++;
4887 break;
4889 break;
4890 default:
4891 skip++;
4892 break;
4894 if (!skip) {
4895 for (i = cnt = 0L; i < quan; i++)
4896 if (!rn2(3))
4897 cnt++;
4899 if (!cnt)
4900 continue;
4901 if (vis)
4902 pline("%s%s %s!",
4903 (cnt == obj->quan) ? "" : (cnt > 1L) ? "Some of "
4904 : "One of ",
4905 (cnt == obj->quan) ? Yname2(obj) : yname(obj),
4906 destroy_strings[dindx][(cnt > 1L)]);
4907 for (i = 0; i < cnt; i++)
4908 m_useup(mtmp, obj);
4911 return tmp;
4915 resist(mtmp, oclass, damage, tell)
4916 struct monst *mtmp;
4917 char oclass;
4918 int damage, tell;
4920 int resisted;
4921 int alev, dlev;
4923 /* attack level */
4924 switch (oclass) {
4925 case WAND_CLASS:
4926 alev = 12;
4927 break;
4928 case TOOL_CLASS:
4929 alev = 10;
4930 break; /* instrument */
4931 case WEAPON_CLASS:
4932 alev = 10;
4933 break; /* artifact */
4934 case SCROLL_CLASS:
4935 alev = 9;
4936 break;
4937 case POTION_CLASS:
4938 alev = 6;
4939 break;
4940 case RING_CLASS:
4941 alev = 5;
4942 break;
4943 default:
4944 alev = u.ulevel;
4945 break; /* spell */
4947 /* defense level */
4948 dlev = (int) mtmp->m_lev;
4949 if (dlev > 50)
4950 dlev = 50;
4951 else if (dlev < 1)
4952 dlev = is_mplayer(mtmp->data) ? u.ulevel : 1;
4954 resisted = rn2(100 + alev - dlev) < mtmp->data->mr;
4955 if (resisted) {
4956 if (tell) {
4957 shieldeff(mtmp->mx, mtmp->my);
4958 pline("%s resists!", Monnam(mtmp));
4960 damage = (damage + 1) / 2;
4963 if (damage) {
4964 mtmp->mhp -= damage;
4965 if (mtmp->mhp < 1) {
4966 if (m_using)
4967 monkilled(mtmp, "", AD_RBRE);
4968 else
4969 killed(mtmp);
4972 return resisted;
4975 #define MAXWISHTRY 5
4977 STATIC_OVL void
4978 wishcmdassist(triesleft)
4979 int triesleft;
4981 static NEARDATA const char *
4982 wishinfo[] = {
4983 "Wish details:",
4985 "Enter the name of an object, such as \"potion of monster detection\",",
4986 "\"scroll labeled README\", \"elven mithril-coat\", or \"Grimtooth\"",
4987 "(without the quotes).",
4989 "For object types which come in stacks, you may specify a plural name",
4990 "such as \"potions of healing\", or specify a count, such as \"1000 gold",
4991 "pieces\", although that aspect of your wish might not be granted.",
4993 "You may also specify various prefix values which might be used to",
4994 "modify the item, such as \"uncursed\" or \"rustproof\" or \"+1\".",
4995 "Most modifiers shown when viewing your inventory can be specified.",
4997 "You may specify 'nothing' to explicitly decline this wish.",
5000 preserve_wishless[] = "Doing so will preserve 'wishless' conduct.",
5001 retry_info[] =
5002 "If you specify an unrecognized object name %s%s time%s,",
5003 retry_too[] = "a randomly chosen item will be granted.",
5004 suppress_cmdassist[] =
5005 "(Suppress this assistance with !cmdassist in your config file.)",
5006 *cardinals[] = { "zero", "one", "two", "three", "four", "five" },
5007 too_many[] = "too many";
5008 int i;
5009 winid win;
5010 char buf[BUFSZ];
5012 win = create_nhwindow(NHW_TEXT);
5013 if (!win)
5014 return;
5015 for (i = 0; i < SIZE(wishinfo) - 1; ++i)
5016 putstr(win, 0, wishinfo[i]);
5017 if (!u.uconduct.wishes)
5018 putstr(win, 0, preserve_wishless);
5019 putstr(win, 0, "");
5020 Sprintf(buf, retry_info,
5021 (triesleft >= 0 && triesleft < SIZE(cardinals))
5022 ? cardinals[triesleft]
5023 : too_many,
5024 (triesleft < MAXWISHTRY) ? " more" : "",
5025 plur(triesleft));
5026 putstr(win, 0, buf);
5027 putstr(win, 0, retry_too);
5028 putstr(win, 0, "");
5029 if (iflags.cmdassist)
5030 putstr(win, 0, suppress_cmdassist);
5031 display_nhwindow(win, FALSE);
5032 destroy_nhwindow(win);
5035 void
5036 makewish()
5038 char buf[BUFSZ], promptbuf[BUFSZ];
5039 struct obj *otmp, nothing;
5040 int tries = 0;
5042 promptbuf[0] = '\0';
5043 nothing = zeroobj; /* lint suppression; only its address matters */
5044 if (flags.verbose)
5045 You("may wish for an object.");
5046 retry:
5047 Strcpy(promptbuf, "For what do you wish");
5048 if (iflags.cmdassist && tries > 0)
5049 Strcat(promptbuf, " (enter 'help' for assistance)");
5050 Strcat(promptbuf, "?");
5051 getlin(promptbuf, buf);
5052 (void) mungspaces(buf);
5053 if (buf[0] == '\033') {
5054 buf[0] = '\0';
5055 } else if (!strcmpi(buf, "help")) {
5056 wishcmdassist(MAXWISHTRY - tries);
5057 goto retry;
5060 * Note: if they wished for and got a non-object successfully,
5061 * otmp == &zeroobj. That includes gold, or an artifact that
5062 * has been denied. Wishing for "nothing" requires a separate
5063 * value to remain distinct.
5065 otmp = readobjnam(buf, &nothing);
5066 if (!otmp) {
5067 pline("Nothing fitting that description exists in the game.");
5068 if (++tries < MAXWISHTRY)
5069 goto retry;
5070 pline1(thats_enough_tries);
5071 otmp = readobjnam((char *) 0, (struct obj *) 0);
5072 if (!otmp)
5073 return; /* for safety; should never happen */
5074 } else if (otmp == &nothing) {
5075 /* explicitly wished for "nothing", presumably attempting
5076 to retain wishless conduct */
5077 return;
5080 /* KMH, conduct */
5081 u.uconduct.wishes++;
5083 if (otmp != &zeroobj) {
5084 const char
5085 *verb = ((Is_airlevel(&u.uz) || u.uinwater) ? "slip" : "drop"),
5086 *oops_msg = (u.uswallow
5087 ? "Oops! %s out of your reach!"
5088 : (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
5089 || levl[u.ux][u.uy].typ < IRONBARS
5090 || levl[u.ux][u.uy].typ >= ICE)
5091 ? "Oops! %s away from you!"
5092 : "Oops! %s to the floor!");
5094 /* The(aobjnam()) is safe since otmp is unidentified -dlc */
5095 (void) hold_another_object(otmp, oops_msg,
5096 The(aobjnam(otmp, verb)),
5097 (const char *) 0);
5098 u.ublesscnt += rn1(100, 50); /* the gods take notice */
5102 /*zap.c*/