shapechanger polymorph bit
[aNetHack.git] / src / zap.c
blobdaa4d5ec6564d84bb8dbef0c7b30c4d474db9677
1 /* NetHack 3.6 zap.c $NHDT-Date: 1457570259 2016/03/10 00:37:39 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.249 $ */
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, 3);
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 learn_it = TRUE;
285 break;
287 case WAN_LOCKING:
288 case SPE_WIZARD_LOCK:
289 wake = closeholdingtrap(mtmp, &learn_it);
290 break;
291 case WAN_PROBING:
292 wake = FALSE;
293 reveal_invis = TRUE;
294 probe_monster(mtmp);
295 learn_it = TRUE;
296 break;
297 case WAN_OPENING:
298 case SPE_KNOCK:
299 wake = FALSE; /* don't want immediate counterattack */
300 if (u.uswallow && mtmp == u.ustuck) {
301 if (is_animal(mtmp->data)) {
302 if (Blind)
303 You_feel("a sudden rush of air!");
304 else
305 pline("%s opens its mouth!", Monnam(mtmp));
307 expels(mtmp, mtmp->data, TRUE);
308 /* zap which hits steed will only release saddle if it
309 doesn't hit a holding or falling trap; playability
310 here overrides the more logical target ordering */
311 } else if (openholdingtrap(mtmp, &learn_it)) {
312 break;
313 } else if (openfallingtrap(mtmp, TRUE, &learn_it)) {
314 /* mtmp might now be on the migrating monsters list */
315 break;
316 } else if ((obj = which_armor(mtmp, W_SADDLE)) != 0) {
317 char buf[BUFSZ];
319 Sprintf(buf, "%s %s", s_suffix(Monnam(mtmp)),
320 distant_name(obj, xname));
321 if (cansee(mtmp->mx, mtmp->my)) {
322 if (!canspotmon(mtmp))
323 Strcpy(buf, An(distant_name(obj, xname)));
324 pline("%s falls to the %s.", buf,
325 surface(mtmp->mx, mtmp->my));
326 } else if (canspotmon(mtmp)) {
327 pline("%s falls off.", buf);
329 obj_extract_self(obj);
330 mdrop_obj(mtmp, obj, FALSE);
332 break;
333 case SPE_HEALING:
334 case SPE_EXTRA_HEALING:
335 reveal_invis = TRUE;
336 if (mtmp->data != &mons[PM_PESTILENCE]) {
337 wake = FALSE; /* wakeup() makes the target angry */
338 mtmp->mhp += d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4);
339 if (mtmp->mhp > mtmp->mhpmax)
340 mtmp->mhp = mtmp->mhpmax;
341 if (mtmp->mblinded) {
342 mtmp->mblinded = 0;
343 mtmp->mcansee = 1;
345 if (canseemon(mtmp)) {
346 if (disguised_mimic) {
347 if (is_obj_mappear(mtmp,STRANGE_OBJECT)) {
348 /* it can do better now */
349 set_mimic_sym(mtmp);
350 newsym(mtmp->mx, mtmp->my);
351 } else
352 mimic_hit_msg(mtmp, otyp);
353 } else
354 pline("%s looks%s better.", Monnam(mtmp),
355 otyp == SPE_EXTRA_HEALING ? " much" : "");
357 if (mtmp->mtame || mtmp->mpeaceful) {
358 adjalign(Role_if(PM_HEALER) ? 1 : sgn(u.ualign.type));
360 } else { /* Pestilence */
361 /* Pestilence will always resist; damage is half of 3d{4,8} */
362 (void) resist(mtmp, otmp->oclass,
363 d(3, otyp == SPE_EXTRA_HEALING ? 8 : 4), TELL);
365 break;
366 case WAN_LIGHT: /* (broken wand) */
367 if (flash_hits_mon(mtmp, otmp)) {
368 learn_it = TRUE;
369 reveal_invis = TRUE;
371 break;
372 case WAN_SLEEP: /* (broken wand) */
373 /* [wakeup() doesn't rouse victims of temporary sleep,
374 so it's okay to leave `wake' set to TRUE here] */
375 reveal_invis = TRUE;
376 if (sleep_monst(mtmp, d(1 + otmp->spe, 12), WAND_CLASS))
377 slept_monst(mtmp);
378 if (!Blind)
379 learn_it = TRUE;
380 break;
381 case SPE_STONE_TO_FLESH:
382 if (monsndx(mtmp->data) == PM_STONE_GOLEM) {
383 char *name = Monnam(mtmp);
385 /* turn into flesh golem */
386 if (newcham(mtmp, &mons[PM_FLESH_GOLEM], FALSE, FALSE)) {
387 if (canseemon(mtmp))
388 pline("%s turns to flesh!", name);
389 } else {
390 if (canseemon(mtmp))
391 pline("%s looks rather fleshy for a moment.", name);
393 } else
394 wake = FALSE;
395 break;
396 case SPE_DRAIN_LIFE:
397 if (disguised_mimic)
398 seemimic(mtmp);
399 dmg = monhp_per_lvl(mtmp);
400 if (dbldam)
401 dmg *= 2;
402 if (otyp == SPE_DRAIN_LIFE)
403 dmg = spell_damage_bonus(dmg);
404 if (resists_drli(mtmp))
405 shieldeff(mtmp->mx, mtmp->my);
406 else if (!resist(mtmp, otmp->oclass, dmg, NOTELL) && mtmp->mhp > 0) {
407 mtmp->mhp -= dmg;
408 mtmp->mhpmax -= dmg;
409 if (mtmp->mhp <= 0 || mtmp->mhpmax <= 0 || mtmp->m_lev < 1)
410 xkilled(mtmp, 1);
411 else {
412 mtmp->m_lev--;
413 if (canseemon(mtmp))
414 pline("%s suddenly seems weaker!", Monnam(mtmp));
417 break;
418 case WAN_NOTHING:
419 wake = FALSE;
420 break;
421 default:
422 impossible("What an interesting effect (%d)", otyp);
423 break;
425 if (wake) {
426 if (mtmp->mhp > 0) {
427 wakeup(mtmp);
428 m_respond(mtmp);
429 if (mtmp->isshk && !*u.ushops)
430 hot_pursuit(mtmp);
431 } else if (mtmp->m_ap_type)
432 seemimic(mtmp); /* might unblock if mimicing a boulder/door */
434 /* note: bhitpos won't be set if swallowed, but that's okay since
435 * reveal_invis will be false. We can't use mtmp->mx, my since it
436 * might be an invisible worm hit on the tail.
438 if (reveal_invis) {
439 if (mtmp->mhp > 0 && cansee(bhitpos.x, bhitpos.y)
440 && !canspotmon(mtmp))
441 map_invisible(bhitpos.x, bhitpos.y);
443 /* if effect was observable then discover the wand type provided
444 that the wand itself has been seen */
445 if (learn_it)
446 learnwand(otmp);
447 return 0;
450 void
451 probe_monster(mtmp)
452 struct monst *mtmp;
454 struct obj *otmp;
456 mstatusline(mtmp);
457 if (notonhead)
458 return; /* don't show minvent for long worm tail */
460 if (mtmp->minvent) {
461 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) {
462 otmp->dknown = 1; /* treat as "seen" */
463 if (Is_container(otmp) || otmp->otyp == STATUE) {
464 otmp->lknown = 1;
465 if (!SchroedingersBox(otmp))
466 otmp->cknown = 1;
469 (void) display_minventory(mtmp, MINV_ALL | MINV_NOLET, (char *) 0);
470 } else {
471 pline("%s is not carrying anything%s.", noit_Monnam(mtmp),
472 (u.uswallow && mtmp == u.ustuck) ? " besides you" : "");
477 * Return the object's physical location. This only makes sense for
478 * objects that are currently on the level (i.e. migrating objects
479 * are nowhere). By default, only things that can be seen (in hero's
480 * inventory, monster's inventory, or on the ground) are reported.
481 * By adding BURIED_TOO and/or CONTAINED_TOO flags, you can also get
482 * the location of buried and contained objects. Note that if an
483 * object is carried by a monster, its reported position may change
484 * from turn to turn. This function returns FALSE if the position
485 * is not available or subject to the constraints above.
487 boolean
488 get_obj_location(obj, xp, yp, locflags)
489 struct obj *obj;
490 xchar *xp, *yp;
491 int locflags;
493 switch (obj->where) {
494 case OBJ_INVENT:
495 *xp = u.ux;
496 *yp = u.uy;
497 return TRUE;
498 case OBJ_FLOOR:
499 *xp = obj->ox;
500 *yp = obj->oy;
501 return TRUE;
502 case OBJ_MINVENT:
503 if (obj->ocarry->mx) {
504 *xp = obj->ocarry->mx;
505 *yp = obj->ocarry->my;
506 return TRUE;
508 break; /* !mx => migrating monster */
509 case OBJ_BURIED:
510 if (locflags & BURIED_TOO) {
511 *xp = obj->ox;
512 *yp = obj->oy;
513 return TRUE;
515 break;
516 case OBJ_CONTAINED:
517 if (locflags & CONTAINED_TOO)
518 return get_obj_location(obj->ocontainer, xp, yp, locflags);
519 break;
521 *xp = *yp = 0;
522 return FALSE;
525 boolean
526 get_mon_location(mon, xp, yp, locflags)
527 struct monst *mon;
528 xchar *xp, *yp;
529 int locflags; /* non-zero means get location even if monster is buried */
531 if (mon == &youmonst) {
532 *xp = u.ux;
533 *yp = u.uy;
534 return TRUE;
535 } else if (mon->mx > 0 && (!mon->mburied || locflags)) {
536 *xp = mon->mx;
537 *yp = mon->my;
538 return TRUE;
539 } else { /* migrating or buried */
540 *xp = *yp = 0;
541 return FALSE;
545 /* used by revive() and animate_statue() */
546 struct monst *
547 montraits(obj, cc)
548 struct obj *obj;
549 coord *cc;
551 struct monst *mtmp = (struct monst *) 0;
552 struct monst *mtmp2 = (struct monst *) 0;
554 if (has_omonst(obj))
555 mtmp2 = get_mtraits(obj, TRUE);
556 if (mtmp2) {
557 /* save_mtraits() validated mtmp2->mnum */
558 mtmp2->data = &mons[mtmp2->mnum];
559 if (mtmp2->mhpmax <= 0 && !is_rider(mtmp2->data))
560 return (struct monst *) 0;
561 mtmp = makemon(mtmp2->data, cc->x, cc->y,
562 NO_MINVENT | MM_NOWAIT | MM_NOCOUNTBIRTH);
563 if (!mtmp)
564 return mtmp;
566 /* heal the monster */
567 if (mtmp->mhpmax > mtmp2->mhpmax && is_rider(mtmp2->data))
568 mtmp2->mhpmax = mtmp->mhpmax;
569 mtmp2->mhp = mtmp2->mhpmax;
570 /* Get these ones from mtmp */
571 mtmp2->minvent = mtmp->minvent; /*redundant*/
572 /* monster ID is available if the monster died in the current
573 game, but will be zero if the corpse was in a bones level
574 (we cleared it when loading bones) */
575 if (mtmp->m_id) {
576 mtmp2->m_id = mtmp->m_id;
577 /* might be bringing quest leader back to life */
578 if (quest_status.leader_is_dead &&
579 /* leader_is_dead implies leader_m_id is valid */
580 mtmp2->m_id == quest_status.leader_m_id)
581 quest_status.leader_is_dead = FALSE;
583 mtmp2->mx = mtmp->mx;
584 mtmp2->my = mtmp->my;
585 mtmp2->mux = mtmp->mux;
586 mtmp2->muy = mtmp->muy;
587 mtmp2->mw = mtmp->mw;
588 mtmp2->wormno = mtmp->wormno;
589 mtmp2->misc_worn_check = mtmp->misc_worn_check;
590 mtmp2->weapon_check = mtmp->weapon_check;
591 mtmp2->mtrapseen = mtmp->mtrapseen;
592 mtmp2->mflee = mtmp->mflee;
593 mtmp2->mburied = mtmp->mburied;
594 mtmp2->mundetected = mtmp->mundetected;
595 mtmp2->mfleetim = mtmp->mfleetim;
596 mtmp2->mlstmv = mtmp->mlstmv;
597 mtmp2->m_ap_type = mtmp->m_ap_type;
598 /* set these ones explicitly */
599 mtmp2->mrevived = 1;
600 mtmp2->mavenge = 0;
601 mtmp2->meating = 0;
602 mtmp2->mleashed = 0;
603 mtmp2->mtrapped = 0;
604 mtmp2->msleeping = 0;
605 mtmp2->mfrozen = 0;
606 mtmp2->mcanmove = 1;
607 /* most cancelled monsters return to normal,
608 but some need to stay cancelled */
609 if (!dmgtype(mtmp2->data, AD_SEDU)
610 && (!SYSOPT_SEDUCE || !dmgtype(mtmp2->data, AD_SSEX)))
611 mtmp2->mcan = 0;
612 mtmp2->mcansee = 1; /* set like in makemon */
613 mtmp2->mblinded = 0;
614 mtmp2->mstun = 0;
615 mtmp2->mconf = 0;
616 replmon(mtmp, mtmp2);
617 newsym(mtmp2->mx, mtmp2->my); /* Might now be invisible */
619 /* in case Protection_from_shape_changers is different
620 now than it was when the traits were stored */
621 restore_cham(mtmp2);
623 return mtmp2;
627 * get_container_location() returns the following information
628 * about the outermost container:
629 * loc argument gets set to:
630 * OBJ_INVENT if in hero's inventory; return 0.
631 * OBJ_FLOOR if on the floor; return 0.
632 * OBJ_BURIED if buried; return 0.
633 * OBJ_MINVENT if in monster's inventory; return monster.
634 * container_nesting is updated with the nesting depth of the containers
635 * if applicable.
637 struct monst *
638 get_container_location(obj, loc, container_nesting)
639 struct obj *obj;
640 int *loc;
641 int *container_nesting;
643 if (!obj || !loc)
644 return 0;
646 if (container_nesting)
647 *container_nesting = 0;
648 while (obj && obj->where == OBJ_CONTAINED) {
649 if (container_nesting)
650 *container_nesting += 1;
651 obj = obj->ocontainer;
653 if (obj) {
654 *loc = obj->where; /* outermost container's location */
655 if (obj->where == OBJ_MINVENT)
656 return obj->ocarry;
658 return (struct monst *) 0;
662 * Attempt to revive the given corpse, return the revived monster if
663 * successful. Note: this does NOT use up the corpse if it fails.
665 struct monst *
666 revive(corpse, by_hero)
667 struct obj *corpse;
668 boolean by_hero;
670 struct monst *mtmp = 0;
671 struct permonst *mptr;
672 struct obj *container;
673 coord xy;
674 xchar x, y;
675 int montype, container_nesting = 0;
677 if (corpse->otyp != CORPSE) {
678 impossible("Attempting to revive %s?", xname(corpse));
679 return (struct monst *) 0;
682 x = y = 0;
683 if (corpse->where != OBJ_CONTAINED) {
684 /* only for invent, minvent, or floor */
685 container = 0;
686 (void) get_obj_location(corpse, &x, &y, 0);
687 } else {
688 /* deal with corpses in [possibly nested] containers */
689 struct monst *carrier;
690 int holder = OBJ_FREE;
692 container = corpse->ocontainer;
693 carrier =
694 get_container_location(container, &holder, &container_nesting);
695 switch (holder) {
696 case OBJ_MINVENT:
697 x = carrier->mx, y = carrier->my;
698 break;
699 case OBJ_INVENT:
700 x = u.ux, y = u.uy;
701 break;
702 case OBJ_FLOOR:
703 (void) get_obj_location(corpse, &x, &y, CONTAINED_TOO);
704 break;
705 default:
706 break; /* x,y are 0 */
709 if (!x || !y ||
710 /* Rules for revival from containers:
711 - the container cannot be locked
712 - the container cannot be heavily nested (>2 is arbitrary)
713 - the container cannot be a statue or bag of holding
714 (except in very rare cases for the latter)
716 (container && (container->olocked || container_nesting > 2
717 || container->otyp == STATUE
718 || (container->otyp == BAG_OF_HOLDING && rn2(40)))))
719 return (struct monst *) 0;
721 /* record the object's location now that we're sure where it is */
722 corpse->ox = x, corpse->oy = y;
724 /* prepare for the monster */
725 montype = corpse->corpsenm;
726 mptr = &mons[montype];
727 /* [should probably handle recorporealization first; if corpse and
728 ghost are at same location, revived creature shouldn't be bumped
729 to an adjacent spot by ghost which joins with it] */
730 if (MON_AT(x, y)) {
731 if (enexto(&xy, x, y, mptr))
732 x = xy.x, y = xy.y;
735 if (mons[montype].mlet == S_EEL && !IS_POOL(levl[x][y].typ)) {
736 if (by_hero && cansee(x,y))
737 pline("%s twitches feebly.",
738 upstart(corpse_xname(corpse, (const char *) 0, CXN_PFX_THE)));
739 return (struct monst *) 0;
742 if (cant_revive(&montype, TRUE, corpse)) {
743 /* make a zombie or doppelganger instead */
744 /* note: montype has changed; mptr keeps old value for newcham() */
745 mtmp = makemon(&mons[montype], x, y, NO_MINVENT | MM_NOWAIT);
746 if (mtmp) {
747 /* skip ghost handling */
748 if (has_omid(corpse))
749 free_omid(corpse);
750 if (has_omonst(corpse))
751 free_omonst(corpse);
752 if (mtmp->cham == PM_DOPPELGANGER) {
753 /* change shape to match the corpse */
754 (void) newcham(mtmp, mptr, FALSE, FALSE);
755 } else if (mtmp->data->mlet == S_ZOMBIE) {
756 mtmp->mhp = mtmp->mhpmax = 100;
757 mon_adjust_speed(mtmp, 2, (struct obj *) 0); /* MFAST */
760 } else if (has_omonst(corpse)) {
761 /* use saved traits */
762 xy.x = x, xy.y = y;
763 mtmp = montraits(corpse, &xy);
764 if (mtmp && mtmp->mtame && !mtmp->isminion)
765 wary_dog(mtmp, TRUE);
766 } else {
767 /* make a new monster */
768 mtmp = makemon(mptr, x, y, NO_MINVENT | MM_NOWAIT | MM_NOCOUNTBIRTH);
770 if (!mtmp)
771 return (struct monst *) 0;
773 /* hiders shouldn't already be re-hidden when they revive */
774 if (mtmp->mundetected) {
775 mtmp->mundetected = 0;
776 newsym(mtmp->mx, mtmp->my);
778 if (mtmp->m_ap_type)
779 seemimic(mtmp);
781 /* if this is caused by the hero there might be a shop charge */
782 if (by_hero) {
783 struct monst *shkp = 0;
785 x = corpse->ox, y = corpse->oy;
786 if (costly_spot(x, y))
787 shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
789 if (cansee(x, y)) {
790 char buf[BUFSZ];
791 unsigned pfx = CXN_PFX_THE;
793 Strcpy(buf, (corpse->quan > 1L) ? "one of " : "");
794 if (carried(corpse) && !corpse->unpaid) {
795 Strcat(buf, "your ");
796 pfx = CXN_NO_PFX;
798 Strcat(buf, corpse_xname(corpse, (const char *) 0, pfx));
799 pline("%s glows iridescently.", upstart(buf));
800 } else if (shkp) {
801 /* need some prior description of the corpse since
802 stolen_value() will refer to the object as "it" */
803 pline("A corpse is resuscitated.");
805 /* don't charge for shopkeeper's own corpse if we just revived him */
806 if (shkp && mtmp != shkp)
807 (void) stolen_value(corpse, x, y, (boolean) shkp->mpeaceful,
808 FALSE);
810 /* [we don't give any comparable message about the corpse for
811 the !by_hero case because caller might have already done so] */
814 /* handle recorporealization of an active ghost */
815 if (has_omid(corpse)) {
816 unsigned m_id;
817 struct monst *ghost;
818 struct obj *otmp;
820 (void) memcpy((genericptr_t) &m_id, (genericptr_t) OMID(corpse),
821 sizeof m_id);
822 ghost = find_mid(m_id, FM_FMON);
823 if (ghost && ghost->data == &mons[PM_GHOST]) {
824 if (canseemon(ghost))
825 pline("%s is suddenly drawn into its former body!",
826 Monnam(ghost));
827 /* transfer the ghost's inventory along with it */
828 while ((otmp = ghost->minvent) != 0) {
829 obj_extract_self(otmp);
830 add_to_minv(mtmp, otmp);
832 /* tame the revived monster if its ghost was tame */
833 if (ghost->mtame && !mtmp->mtame) {
834 if (tamedog(mtmp, (struct obj *) 0)) {
835 /* ghost's edog data is ignored */
836 mtmp->mtame = ghost->mtame;
839 /* was ghost, now alive, it's all very confusing */
840 mtmp->mconf = 1;
841 /* separate ghost monster no longer exists */
842 mongone(ghost);
844 free_omid(corpse);
847 /* monster retains its name */
848 if (has_oname(corpse) && !unique_corpstat(mtmp->data))
849 mtmp = christen_monst(mtmp, ONAME(corpse));
850 /* partially eaten corpse yields wounded monster */
851 if (corpse->oeaten)
852 mtmp->mhp = eaten_stat(mtmp->mhp, corpse);
853 /* track that this monster was revived at least once */
854 mtmp->mrevived = 1;
856 /* finally, get rid of the corpse--it's gone now */
857 switch (corpse->where) {
858 case OBJ_INVENT:
859 useup(corpse);
860 break;
861 case OBJ_FLOOR:
862 /* in case MON_AT+enexto for invisible mon */
863 x = corpse->ox, y = corpse->oy;
864 /* not useupf(), which charges */
865 if (corpse->quan > 1L)
866 corpse = splitobj(corpse, 1L);
867 delobj(corpse);
868 newsym(x, y);
869 break;
870 case OBJ_MINVENT:
871 m_useup(corpse->ocarry, corpse);
872 break;
873 case OBJ_CONTAINED:
874 obj_extract_self(corpse);
875 obfree(corpse, (struct obj *) 0);
876 break;
877 default:
878 panic("revive");
881 return mtmp;
884 STATIC_OVL void
885 revive_egg(obj)
886 struct obj *obj;
889 * Note: generic eggs with corpsenm set to NON_PM will never hatch.
891 if (obj->otyp != EGG)
892 return;
893 if (obj->corpsenm != NON_PM && !dead_species(obj->corpsenm, TRUE))
894 attach_egg_hatch_timeout(obj, 0L);
897 /* try to revive all corpses and eggs carried by `mon' */
899 unturn_dead(mon)
900 struct monst *mon;
902 struct obj *otmp, *otmp2;
903 struct monst *mtmp2;
904 char owner[BUFSZ], corpse[BUFSZ];
905 boolean youseeit;
906 int once = 0, res = 0;
908 youseeit = (mon == &youmonst) ? TRUE : canseemon(mon);
909 otmp2 = (mon == &youmonst) ? invent : mon->minvent;
911 while ((otmp = otmp2) != 0) {
912 otmp2 = otmp->nobj;
913 if (otmp->otyp == EGG)
914 revive_egg(otmp);
915 if (otmp->otyp != CORPSE)
916 continue;
917 /* save the name; the object is liable to go away */
918 if (youseeit)
919 Strcpy(corpse,
920 corpse_xname(otmp, (const char *) 0, CXN_SINGULAR));
922 /* for a merged group, only one is revived; should this be fixed? */
923 if ((mtmp2 = revive(otmp, !context.mon_moving)) != 0) {
924 ++res;
925 if (youseeit) {
926 if (!once++)
927 Strcpy(owner, (mon == &youmonst) ? "Your"
928 : s_suffix(Monnam(mon)));
929 pline("%s %s suddenly comes alive!", owner, corpse);
930 } else if (canseemon(mtmp2))
931 pline("%s suddenly appears!", Amonnam(mtmp2));
934 return res;
937 /* cancel obj, possibly carried by you or a monster */
938 void
939 cancel_item(obj)
940 register struct obj *obj;
942 boolean u_ring = (obj == uleft || obj == uright);
943 int otyp = obj->otyp;
945 switch (otyp) {
946 case RIN_GAIN_STRENGTH:
947 if ((obj->owornmask & W_RING) && u_ring) {
948 ABON(A_STR) -= obj->spe;
949 context.botl = 1;
951 break;
952 case RIN_GAIN_CONSTITUTION:
953 if ((obj->owornmask & W_RING) && u_ring) {
954 ABON(A_CON) -= obj->spe;
955 context.botl = 1;
957 break;
958 case RIN_ADORNMENT:
959 if ((obj->owornmask & W_RING) && u_ring) {
960 ABON(A_CHA) -= obj->spe;
961 context.botl = 1;
963 break;
964 case RIN_INCREASE_ACCURACY:
965 if ((obj->owornmask & W_RING) && u_ring)
966 u.uhitinc -= obj->spe;
967 break;
968 case RIN_INCREASE_DAMAGE:
969 if ((obj->owornmask & W_RING) && u_ring)
970 u.udaminc -= obj->spe;
971 break;
972 case GAUNTLETS_OF_DEXTERITY:
973 if ((obj->owornmask & W_ARMG) && (obj == uarmg)) {
974 ABON(A_DEX) -= obj->spe;
975 context.botl = 1;
977 break;
978 case HELM_OF_BRILLIANCE:
979 if ((obj->owornmask & W_ARMH) && (obj == uarmh)) {
980 ABON(A_INT) -= obj->spe;
981 ABON(A_WIS) -= obj->spe;
982 context.botl = 1;
984 break;
985 /* case RIN_PROTECTION: not needed */
987 if (objects[otyp].oc_magic
988 || (obj->spe && (obj->oclass == ARMOR_CLASS
989 || obj->oclass == WEAPON_CLASS || is_weptool(obj)))
990 || otyp == POT_ACID
991 || otyp == POT_SICKNESS
992 || (otyp == POT_WATER && (obj->blessed || obj->cursed))) {
993 if (obj->spe != ((obj->oclass == WAND_CLASS) ? -1 : 0)
994 && otyp != WAN_CANCELLATION /* can't cancel cancellation */
995 && otyp != MAGIC_LAMP /* cancelling doesn't remove djinni */
996 && otyp != CANDELABRUM_OF_INVOCATION) {
997 costly_alteration(obj, COST_CANCEL);
998 obj->spe = (obj->oclass == WAND_CLASS) ? -1 : 0;
1000 switch (obj->oclass) {
1001 case SCROLL_CLASS:
1002 costly_alteration(obj, COST_CANCEL);
1003 obj->otyp = SCR_BLANK_PAPER;
1004 obj->spe = 0;
1005 break;
1006 case SPBOOK_CLASS:
1007 if (otyp != SPE_CANCELLATION && otyp != SPE_NOVEL
1008 && otyp != SPE_BOOK_OF_THE_DEAD) {
1009 costly_alteration(obj, COST_CANCEL);
1010 obj->otyp = SPE_BLANK_PAPER;
1012 break;
1013 case POTION_CLASS:
1014 costly_alteration(obj,
1015 (otyp != POT_WATER)
1016 ? COST_CANCEL
1017 : obj->cursed ? COST_UNCURS : COST_UNBLSS);
1018 if (otyp == POT_SICKNESS || otyp == POT_SEE_INVISIBLE) {
1019 /* sickness is "biologically contaminated" fruit juice;
1020 cancel it and it just becomes fruit juice...
1021 whereas see invisible tastes like "enchanted" fruit
1022 juice, it similarly cancels */
1023 obj->otyp = POT_FRUIT_JUICE;
1024 } else {
1025 obj->otyp = POT_WATER;
1026 obj->odiluted = 0; /* same as any other water */
1028 break;
1031 unbless(obj);
1032 uncurse(obj);
1033 return;
1036 /* Remove a positive enchantment or charge from obj,
1037 * possibly carried by you or a monster
1039 boolean
1040 drain_item(obj)
1041 register struct obj *obj;
1043 boolean u_ring;
1045 /* Is this a charged/enchanted object? */
1046 if (!obj
1047 || (!objects[obj->otyp].oc_charged && obj->oclass != WEAPON_CLASS
1048 && obj->oclass != ARMOR_CLASS && !is_weptool(obj))
1049 || obj->spe <= 0)
1050 return FALSE;
1051 if (defends(AD_DRLI, obj) || defends_when_carried(AD_DRLI, obj)
1052 || obj_resists(obj, 10, 90))
1053 return FALSE;
1055 /* Charge for the cost of the object */
1056 costly_alteration(obj, COST_DRAIN);
1058 /* Drain the object and any implied effects */
1059 obj->spe--;
1060 u_ring = (obj == uleft) || (obj == uright);
1061 switch (obj->otyp) {
1062 case RIN_GAIN_STRENGTH:
1063 if ((obj->owornmask & W_RING) && u_ring) {
1064 ABON(A_STR)--;
1065 context.botl = 1;
1067 break;
1068 case RIN_GAIN_CONSTITUTION:
1069 if ((obj->owornmask & W_RING) && u_ring) {
1070 ABON(A_CON)--;
1071 context.botl = 1;
1073 break;
1074 case RIN_ADORNMENT:
1075 if ((obj->owornmask & W_RING) && u_ring) {
1076 ABON(A_CHA)--;
1077 context.botl = 1;
1079 break;
1080 case RIN_INCREASE_ACCURACY:
1081 if ((obj->owornmask & W_RING) && u_ring)
1082 u.uhitinc--;
1083 break;
1084 case RIN_INCREASE_DAMAGE:
1085 if ((obj->owornmask & W_RING) && u_ring)
1086 u.udaminc--;
1087 break;
1088 case HELM_OF_BRILLIANCE:
1089 if ((obj->owornmask & W_ARMH) && (obj == uarmh)) {
1090 ABON(A_INT)--;
1091 ABON(A_WIS)--;
1092 context.botl = 1;
1094 break;
1095 case GAUNTLETS_OF_DEXTERITY:
1096 if ((obj->owornmask & W_ARMG) && (obj == uarmg)) {
1097 ABON(A_DEX)--;
1098 context.botl = 1;
1100 break;
1101 case RIN_PROTECTION:
1102 context.botl = 1;
1103 break;
1105 if (carried(obj))
1106 update_inventory();
1107 return TRUE;
1110 boolean
1111 obj_resists(obj, ochance, achance)
1112 struct obj *obj;
1113 int ochance, achance; /* percent chance for ordinary objects, artifacts */
1115 if (obj->otyp == AMULET_OF_YENDOR
1116 || obj->otyp == SPE_BOOK_OF_THE_DEAD
1117 || obj->otyp == CANDELABRUM_OF_INVOCATION
1118 || obj->otyp == BELL_OF_OPENING
1119 || (obj->otyp == CORPSE && is_rider(&mons[obj->corpsenm]))) {
1120 return TRUE;
1121 } else {
1122 int chance = rn2(100);
1124 return (boolean) (chance < (obj->oartifact ? achance : ochance));
1128 boolean
1129 obj_shudders(obj)
1130 struct obj *obj;
1132 int zap_odds;
1134 if (context.bypasses && obj->bypass)
1135 return FALSE;
1137 if (obj->oclass == WAND_CLASS)
1138 zap_odds = 3; /* half-life = 2 zaps */
1139 else if (obj->cursed)
1140 zap_odds = 3; /* half-life = 2 zaps */
1141 else if (obj->blessed)
1142 zap_odds = 12; /* half-life = 8 zaps */
1143 else
1144 zap_odds = 8; /* half-life = 6 zaps */
1146 /* adjust for "large" quantities of identical things */
1147 if (obj->quan > 4L)
1148 zap_odds /= 2;
1150 return (boolean) !rn2(zap_odds);
1153 /* Use up at least minwt number of things made of material mat.
1154 * There's also a chance that other stuff will be used up. Finally,
1155 * there's a random factor here to keep from always using the stuff
1156 * at the top of the pile.
1158 STATIC_OVL void
1159 polyuse(objhdr, mat, minwt)
1160 struct obj *objhdr;
1161 int mat, minwt;
1163 register struct obj *otmp, *otmp2;
1165 for (otmp = objhdr; minwt > 0 && otmp; otmp = otmp2) {
1166 otmp2 = otmp->nexthere;
1167 if (context.bypasses && otmp->bypass)
1168 continue;
1169 if (otmp == uball || otmp == uchain)
1170 continue;
1171 if (obj_resists(otmp, 0, 0))
1172 continue; /* preserve unique objects */
1173 #ifdef MAIL
1174 if (otmp->otyp == SCR_MAIL)
1175 continue;
1176 #endif
1178 if (((int) objects[otmp->otyp].oc_material == mat)
1179 == (rn2(minwt + 1) != 0)) {
1180 /* appropriately add damage to bill */
1181 if (costly_spot(otmp->ox, otmp->oy)) {
1182 if (*u.ushops)
1183 addtobill(otmp, FALSE, FALSE, FALSE);
1184 else
1185 (void) stolen_value(otmp, otmp->ox, otmp->oy, FALSE,
1186 FALSE);
1188 if (otmp->quan < LARGEST_INT)
1189 minwt -= (int) otmp->quan;
1190 else
1191 minwt = 0;
1192 delobj(otmp);
1198 * Polymorph some of the stuff in this pile into a monster, preferably
1199 * a golem of the kind okind.
1201 STATIC_OVL void
1202 create_polymon(obj, okind)
1203 struct obj *obj;
1204 int okind;
1206 struct permonst *mdat = (struct permonst *) 0;
1207 struct monst *mtmp;
1208 const char *material;
1209 int pm_index;
1211 if (context.bypasses) {
1212 /* this is approximate because the "no golems" !obj->nexthere
1213 check below doesn't understand bypassed objects; but it
1214 should suffice since bypassed objects always end up as a
1215 consecutive group at the top of their pile */
1216 while (obj && obj->bypass)
1217 obj = obj->nexthere;
1220 /* no golems if you zap only one object -- not enough stuff */
1221 if (!obj || (!obj->nexthere && obj->quan == 1L))
1222 return;
1224 /* some of these choices are arbitrary */
1225 switch (okind) {
1226 case IRON:
1227 case METAL:
1228 case MITHRIL:
1229 pm_index = PM_IRON_GOLEM;
1230 material = "metal ";
1231 break;
1232 case COPPER:
1233 case SILVER:
1234 case PLATINUM:
1235 case GEMSTONE:
1236 case MINERAL:
1237 pm_index = rn2(2) ? PM_STONE_GOLEM : PM_CLAY_GOLEM;
1238 material = "lithic ";
1239 break;
1240 case 0:
1241 case FLESH:
1242 /* there is no flesh type, but all food is type 0, so we use it */
1243 pm_index = PM_FLESH_GOLEM;
1244 material = "organic ";
1245 break;
1246 case WOOD:
1247 pm_index = PM_WOOD_GOLEM;
1248 material = "wood ";
1249 break;
1250 case LEATHER:
1251 pm_index = PM_LEATHER_GOLEM;
1252 material = "leather ";
1253 break;
1254 case CLOTH:
1255 pm_index = PM_ROPE_GOLEM;
1256 material = "cloth ";
1257 break;
1258 case BONE:
1259 pm_index = PM_SKELETON; /* nearest thing to "bone golem" */
1260 material = "bony ";
1261 break;
1262 case GOLD:
1263 pm_index = PM_GOLD_GOLEM;
1264 material = "gold ";
1265 break;
1266 case GLASS:
1267 pm_index = PM_GLASS_GOLEM;
1268 material = "glassy ";
1269 break;
1270 case PAPER:
1271 pm_index = PM_PAPER_GOLEM;
1272 material = "paper ";
1273 break;
1274 default:
1275 /* if all else fails... */
1276 pm_index = PM_STRAW_GOLEM;
1277 material = "";
1278 break;
1281 if (!(mvitals[pm_index].mvflags & G_GENOD))
1282 mdat = &mons[pm_index];
1284 mtmp = makemon(mdat, obj->ox, obj->oy, NO_MM_FLAGS);
1285 polyuse(obj, okind, (int) mons[pm_index].cwt);
1287 if (mtmp && cansee(mtmp->mx, mtmp->my)) {
1288 pline("Some %sobjects meld, and %s arises from the pile!", material,
1289 a_monnam(mtmp));
1293 /* Assumes obj is on the floor. */
1294 void
1295 do_osshock(obj)
1296 struct obj *obj;
1298 long i;
1300 #ifdef MAIL
1301 if (obj->otyp == SCR_MAIL)
1302 return;
1303 #endif
1304 obj_zapped = TRUE;
1306 if (poly_zapped < 0) {
1307 /* some may metamorphosize */
1308 for (i = obj->quan; i; i--)
1309 if (!rn2(Luck + 45)) {
1310 poly_zapped = objects[obj->otyp].oc_material;
1311 break;
1315 /* if quan > 1 then some will survive intact */
1316 if (obj->quan > 1L) {
1317 if (obj->quan > LARGEST_INT)
1318 obj = splitobj(obj, (long) rnd(30000));
1319 else
1320 obj = splitobj(obj, (long) rnd((int) obj->quan - 1));
1323 /* appropriately add damage to bill */
1324 if (costly_spot(obj->ox, obj->oy)) {
1325 if (*u.ushops)
1326 addtobill(obj, FALSE, FALSE, FALSE);
1327 else
1328 (void) stolen_value(obj, obj->ox, obj->oy, FALSE, FALSE);
1331 /* zap the object */
1332 delobj(obj);
1335 /* classes of items whose current charge count carries over across polymorph
1337 static const char charged_objs[] = { WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS,
1338 '\0' };
1341 * Polymorph the object to the given object ID. If the ID is STRANGE_OBJECT
1342 * then pick random object from the source's class (this is the standard
1343 * "polymorph" case). If ID is set to a specific object, inhibit fusing
1344 * n objects into 1. This could have been added as a flag, but currently
1345 * it is tied to not being the standard polymorph case. The new polymorphed
1346 * object replaces obj in its link chains. Return value is a pointer to
1347 * the new object.
1349 * This should be safe to call for an object anywhere.
1351 struct obj *
1352 poly_obj(obj, id)
1353 struct obj *obj;
1354 int id;
1356 struct obj *otmp;
1357 xchar ox, oy;
1358 boolean can_merge = (id == STRANGE_OBJECT);
1359 int obj_location = obj->where;
1361 if (obj->otyp == BOULDER)
1362 sokoban_guilt();
1363 if (id == STRANGE_OBJECT) { /* preserve symbol */
1364 int try_limit = 3;
1365 unsigned magic_obj = objects[obj->otyp].oc_magic;
1367 if (obj->otyp == UNICORN_HORN && obj->degraded_horn)
1368 magic_obj = 0;
1369 /* Try up to 3 times to make the magic-or-not status of
1370 the new item be the same as it was for the old one. */
1371 otmp = (struct obj *) 0;
1372 do {
1373 if (otmp)
1374 delobj(otmp);
1375 otmp = mkobj(obj->oclass, FALSE);
1376 } while (--try_limit > 0
1377 && objects[otmp->otyp].oc_magic != magic_obj);
1378 } else {
1379 /* literally replace obj with this new thing */
1380 otmp = mksobj(id, FALSE, FALSE);
1381 /* Actually more things use corpsenm but they polymorph differently */
1382 #define USES_CORPSENM(typ) \
1383 ((typ) == CORPSE || (typ) == STATUE || (typ) == FIGURINE)
1385 if (USES_CORPSENM(obj->otyp) && USES_CORPSENM(id))
1386 set_corpsenm(otmp, obj->corpsenm);
1387 #undef USES_CORPSENM
1390 /* preserve quantity */
1391 otmp->quan = obj->quan;
1392 /* preserve the shopkeepers (lack of) interest */
1393 otmp->no_charge = obj->no_charge;
1394 /* preserve inventory letter if in inventory */
1395 if (obj_location == OBJ_INVENT)
1396 otmp->invlet = obj->invlet;
1397 #ifdef MAIL
1398 /* You can't send yourself 100 mail messages and then
1399 * polymorph them into useful scrolls
1401 if (obj->otyp == SCR_MAIL) {
1402 otmp->otyp = SCR_MAIL;
1403 otmp->spe = 1;
1405 #endif
1407 /* avoid abusing eggs laid by you */
1408 if (obj->otyp == EGG && obj->spe) {
1409 int mnum, tryct = 100;
1411 /* first, turn into a generic egg */
1412 if (otmp->otyp == EGG)
1413 kill_egg(otmp);
1414 else {
1415 otmp->otyp = EGG;
1416 otmp->owt = weight(otmp);
1418 otmp->corpsenm = NON_PM;
1419 otmp->spe = 0;
1421 /* now change it into something laid by the hero */
1422 while (tryct--) {
1423 mnum = can_be_hatched(random_monster());
1424 if (mnum != NON_PM && !dead_species(mnum, TRUE)) {
1425 otmp->spe = 1; /* laid by hero */
1426 set_corpsenm(otmp, mnum); /* also sets hatch timer */
1427 break;
1432 /* keep special fields (including charges on wands) */
1433 if (index(charged_objs, otmp->oclass))
1434 otmp->spe = obj->spe;
1435 otmp->recharged = obj->recharged;
1437 otmp->cursed = obj->cursed;
1438 otmp->blessed = obj->blessed;
1440 if (erosion_matters(otmp)) {
1441 if (is_flammable(otmp) || is_rustprone(otmp))
1442 otmp->oeroded = obj->oeroded;
1443 if (is_corrodeable(otmp) || is_rottable(otmp))
1444 otmp->oeroded2 = obj->oeroded2;
1445 if (is_damageable(otmp))
1446 otmp->oerodeproof = obj->oerodeproof;
1449 /* Keep chest/box traps and poisoned ammo if we may */
1450 if (obj->otrapped && Is_box(otmp))
1451 otmp->otrapped = TRUE;
1453 if (obj->opoisoned && is_poisonable(otmp))
1454 otmp->opoisoned = TRUE;
1456 if (id == STRANGE_OBJECT && obj->otyp == CORPSE) {
1457 /* turn crocodile corpses into shoes */
1458 if (obj->corpsenm == PM_CROCODILE) {
1459 otmp->otyp = LOW_BOOTS;
1460 otmp->oclass = ARMOR_CLASS;
1461 otmp->spe = 0;
1462 otmp->oeroded = 0;
1463 otmp->oerodeproof = TRUE;
1464 otmp->quan = 1L;
1465 otmp->cursed = FALSE;
1469 /* no box contents --KAA */
1470 if (Has_contents(otmp))
1471 delete_contents(otmp);
1473 /* 'n' merged objects may be fused into 1 object */
1474 if (otmp->quan > 1L && (!objects[otmp->otyp].oc_merge
1475 || (can_merge && otmp->quan > (long) rn2(1000))))
1476 otmp->quan = 1L;
1478 switch (otmp->oclass) {
1479 case TOOL_CLASS:
1480 if (otmp->otyp == MAGIC_LAMP) {
1481 otmp->otyp = OIL_LAMP;
1482 otmp->age = 1500L; /* "best" oil lamp possible */
1483 } else if (otmp->otyp == MAGIC_MARKER) {
1484 otmp->recharged = 1; /* degraded quality */
1486 /* don't care about the recharge count of other tools */
1487 break;
1489 case WAND_CLASS:
1490 while (otmp->otyp == WAN_WISHING || otmp->otyp == WAN_POLYMORPH)
1491 otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING);
1492 /* altering the object tends to degrade its quality
1493 (analogous to spellbook `read count' handling) */
1494 if ((int) otmp->recharged < rn2(7)) /* recharge_limit */
1495 otmp->recharged++;
1496 break;
1498 case POTION_CLASS:
1499 while (otmp->otyp == POT_POLYMORPH)
1500 otmp->otyp = rnd_class(POT_GAIN_ABILITY, POT_WATER);
1501 break;
1503 case SPBOOK_CLASS:
1504 while (otmp->otyp == SPE_POLYMORPH)
1505 otmp->otyp = rnd_class(SPE_DIG, SPE_BLANK_PAPER);
1506 /* reduce spellbook abuse; non-blank books degrade */
1507 if (otmp->otyp != SPE_BLANK_PAPER) {
1508 otmp->spestudied = obj->spestudied + 1;
1509 if (otmp->spestudied > MAX_SPELL_STUDY) {
1510 otmp->otyp = SPE_BLANK_PAPER;
1511 /* writing a new book over it will yield an unstudied
1512 one; re-polymorphing this one as-is may or may not
1513 get something non-blank */
1514 otmp->spestudied = rn2(otmp->spestudied);
1517 break;
1519 case GEM_CLASS:
1520 if (otmp->quan > (long) rnd(4)
1521 && objects[obj->otyp].oc_material == MINERAL
1522 && objects[otmp->otyp].oc_material != MINERAL) {
1523 otmp->otyp = ROCK; /* transmutation backfired */
1524 otmp->quan /= 2L; /* some material has been lost */
1526 break;
1529 /* update the weight */
1530 otmp->owt = weight(otmp);
1532 /* handle polymorph of worn item: stone-to-flesh cast on self can
1533 affect multiple objects at once, but their new forms won't
1534 produce any side-effects; a single worn item dipped into potion
1535 of polymorph can produce side-effects but those won't yield out
1536 of sequence messages because current polymorph is finished */
1537 if (obj_location == OBJ_INVENT && obj->owornmask) {
1538 long old_wornmask = obj->owornmask & ~(W_ART | W_ARTI),
1539 new_wornmask = wearslot(otmp);
1540 boolean was_twohanded = bimanual(obj), was_twoweap = u.twoweap;
1542 remove_worn_item(obj, TRUE);
1543 /* if the new form can be worn in the same slot, make it so
1544 [possible extension: if it could be worn in some other
1545 slot which is currently unfilled, wear it there instead] */
1546 if ((old_wornmask & W_QUIVER) != 0L) {
1547 setuqwep(otmp);
1548 } else if ((old_wornmask & W_SWAPWEP) != 0L) {
1549 if (was_twohanded || !bimanual(otmp))
1550 setuswapwep(otmp);
1551 if (was_twoweap && uswapwep)
1552 u.twoweap = TRUE;
1553 } else if ((old_wornmask & W_WEP) != 0L) {
1554 if (was_twohanded || !bimanual(otmp) || !uarms)
1555 setuwep(otmp);
1556 if (was_twoweap && uwep && !bimanual(uwep))
1557 u.twoweap = TRUE;
1558 } else if ((old_wornmask & new_wornmask) != 0L) {
1559 new_wornmask &= old_wornmask;
1560 setworn(otmp, new_wornmask);
1561 set_wear(otmp); /* Armor_on() for side-effects */
1565 /* ** we are now done adjusting the object ** */
1567 /* swap otmp for obj */
1568 replace_object(obj, otmp);
1569 if (obj_location == OBJ_INVENT) {
1571 * We may need to do extra adjustments for the hero if we're
1572 * messing with the hero's inventory. The following calls are
1573 * equivalent to calling freeinv on obj and addinv on otmp,
1574 * while doing an in-place swap of the actual objects.
1576 freeinv_core(obj);
1577 addinv_core1(otmp);
1578 addinv_core2(otmp);
1579 } else if (obj_location == OBJ_FLOOR) {
1580 ox = otmp->ox, oy = otmp->oy; /* set by replace_object() */
1581 if (obj->otyp == BOULDER && otmp->otyp != BOULDER
1582 && !does_block(ox, oy, &levl[ox][oy]))
1583 unblock_point(ox, oy);
1584 else if (obj->otyp != BOULDER && otmp->otyp == BOULDER)
1585 /* (checking does_block() here would be redundant) */
1586 block_point(ox, oy);
1589 if ((!carried(otmp) || obj->unpaid)
1590 && get_obj_location(otmp, &ox, &oy, BURIED_TOO | CONTAINED_TOO)
1591 && costly_spot(ox, oy)) {
1592 register struct monst *shkp =
1593 shop_keeper(*in_rooms(ox, oy, SHOPBASE));
1595 if ((!obj->no_charge
1596 || (Has_contents(obj)
1597 && (contained_cost(obj, shkp, 0L, FALSE, FALSE) != 0L)))
1598 && inhishop(shkp)) {
1599 if (shkp->mpeaceful) {
1600 if (*u.ushops
1601 && *in_rooms(u.ux, u.uy, 0)
1602 == *in_rooms(shkp->mx, shkp->my, 0)
1603 && !costly_spot(u.ux, u.uy))
1604 make_angry_shk(shkp, ox, oy);
1605 else {
1606 pline("%s gets angry!", Monnam(shkp));
1607 hot_pursuit(shkp);
1609 } else
1610 Norep("%s is furious!", Monnam(shkp));
1613 delobj(obj);
1614 return otmp;
1617 /* stone-to-flesh spell hits and maybe transforms or animates obj */
1618 STATIC_OVL int
1619 stone_to_flesh_obj(obj)
1620 struct obj *obj;
1622 int res = 1; /* affected object by default */
1623 struct permonst *ptr;
1624 struct monst *mon;
1625 struct obj *item;
1626 xchar oox, ooy;
1627 boolean smell = FALSE, golem_xform = FALSE;
1629 if (objects[obj->otyp].oc_material != MINERAL
1630 && objects[obj->otyp].oc_material != GEMSTONE)
1631 return 0;
1632 /* Heart of Ahriman usually resists; ordinary items rarely do */
1633 if (obj_resists(obj, 2, 98))
1634 return 0;
1636 (void) get_obj_location(obj, &oox, &ooy, 0);
1637 /* add more if stone objects are added.. */
1638 switch (objects[obj->otyp].oc_class) {
1639 case ROCK_CLASS: /* boulders and statues */
1640 case TOOL_CLASS: /* figurines */
1641 if (obj->otyp == BOULDER) {
1642 obj = poly_obj(obj, HUGE_CHUNK_OF_MEAT);
1643 smell = TRUE;
1644 } else if (obj->otyp == STATUE || obj->otyp == FIGURINE) {
1645 ptr = &mons[obj->corpsenm];
1646 if (is_golem(ptr)) {
1647 golem_xform = (ptr != &mons[PM_FLESH_GOLEM]);
1648 } else if (vegetarian(ptr)) {
1649 /* Don't animate monsters that aren't flesh */
1650 obj = poly_obj(obj, MEATBALL);
1651 smell = TRUE;
1652 break;
1654 if (obj->otyp == STATUE) {
1655 /* animate_statue() forces all golems to become flesh golems
1657 mon = animate_statue(obj, oox, ooy, ANIMATE_SPELL, (int *) 0);
1658 } else { /* (obj->otyp == FIGURINE) */
1659 if (golem_xform)
1660 ptr = &mons[PM_FLESH_GOLEM];
1661 mon = makemon(ptr, oox, ooy, NO_MINVENT);
1662 if (mon) {
1663 if (costly_spot(oox, ooy) && !obj->no_charge) {
1664 if (costly_spot(u.ux, u.uy))
1665 addtobill(obj, carried(obj), FALSE, FALSE);
1666 else
1667 stolen_value(obj, oox, ooy, TRUE, FALSE);
1669 if (obj->timed)
1670 obj_stop_timers(obj);
1671 if (carried(obj))
1672 useup(obj);
1673 else
1674 delobj(obj);
1675 if (cansee(mon->mx, mon->my))
1676 pline_The("figurine %sanimates!",
1677 golem_xform ? "turns to flesh and " : "");
1680 if (mon) {
1681 ptr = mon->data;
1682 /* this golem handling is redundant... */
1683 if (is_golem(ptr) && ptr != &mons[PM_FLESH_GOLEM])
1684 (void) newcham(mon, &mons[PM_FLESH_GOLEM], TRUE, FALSE);
1685 } else if ((ptr->geno & (G_NOCORPSE | G_UNIQ)) != 0) {
1686 /* didn't revive but can't leave corpse either */
1687 res = 0;
1688 } else {
1689 /* unlikely to get here since genociding monsters also
1690 sets the G_NOCORPSE flag; drop statue's contents */
1691 while ((item = obj->cobj) != 0) {
1692 bypass_obj(item); /* make stone-to-flesh miss it */
1693 obj_extract_self(item);
1694 place_object(item, oox, ooy);
1696 obj = poly_obj(obj, CORPSE);
1698 } else { /* miscellaneous tool or unexpected rock... */
1699 res = 0;
1701 break;
1702 /* maybe add weird things to become? */
1703 case RING_CLASS: /* some of the rings are stone */
1704 obj = poly_obj(obj, MEAT_RING);
1705 smell = TRUE;
1706 break;
1707 case WAND_CLASS: /* marble wand */
1708 obj = poly_obj(obj, MEAT_STICK);
1709 smell = TRUE;
1710 break;
1711 case GEM_CLASS: /* stones & gems */
1712 obj = poly_obj(obj, MEATBALL);
1713 smell = TRUE;
1714 break;
1715 case WEAPON_CLASS: /* crysknife */
1716 /*FALLTHRU*/
1717 default:
1718 res = 0;
1719 break;
1722 if (smell) {
1723 /* non-meat eaters smell meat, meat eaters smell its flavor;
1724 monks are considered non-meat eaters regardless of behavior;
1725 other roles are non-meat eaters if they haven't broken
1726 vegetarian conduct yet (or if poly'd into non-carnivorous/
1727 non-omnivorous form, regardless of whether it's herbivorous,
1728 non-eating, or something stranger) */
1729 if (Role_if(PM_MONK) || !u.uconduct.unvegetarian
1730 || !carnivorous(youmonst.data))
1731 Norep("You smell the odor of meat.");
1732 else
1733 Norep("You smell a delicious smell.");
1735 newsym(oox, ooy);
1736 return res;
1740 * Object obj was hit by the effect of the wand/spell otmp. Return
1741 * non-zero if the wand/spell had any effect.
1744 bhito(obj, otmp)
1745 struct obj *obj, *otmp;
1747 int res = 1; /* affected object by default */
1748 boolean learn_it = FALSE, maybelearnit;
1750 /* fundamental: a wand effect hitting itself doesn't do anything;
1751 otherwise we need to guard against accessing otmp after something
1752 strange has happened to it (along the lines of polymorph or
1753 stone-to-flesh [which aren't good examples since polymorph wands
1754 aren't affected by polymorph zaps and stone-to-flesh isn't
1755 available in wand form, but the concept still applies...]) */
1756 if (obj == otmp)
1757 return 0;
1759 if (obj->bypass) {
1760 /* The bypass bit is currently only used as follows:
1762 * POLYMORPH - When a monster being polymorphed drops something
1763 * from its inventory as a result of the change.
1764 * If the items fall to the floor, they are not
1765 * subject to direct subsequent polymorphing
1766 * themselves on that same zap. This makes it
1767 * consistent with items that remain in the
1768 * monster's inventory. They are not polymorphed
1769 * either.
1770 * UNDEAD_TURNING - When an undead creature gets killed via
1771 * undead turning, prevent its corpse from being
1772 * immediately revived by the same effect.
1773 * STONE_TO_FLESH - If a statue can't be revived, its
1774 * contents get dropped before turning it into
1775 * meat; prevent those contents from being hit.
1776 * retouch_equipment() - bypass flag is used to track which
1777 * items have been handled (bhito isn't involved).
1778 * menu_drop(), askchain() - inventory traversal where multiple
1779 * Drop can alter the invent chain while traversal
1780 * is in progress (bhito isn't involved).
1782 * The bypass bit on all objects is reset each turn, whenever
1783 * context.bypasses is set.
1785 * We check the obj->bypass bit above AND context.bypasses
1786 * as a safeguard against any stray occurrence left in an obj
1787 * struct someplace, although that should never happen.
1789 if (context.bypasses) {
1790 return 0;
1791 } else {
1792 debugpline1("%s for a moment.", Tobjnam(obj, "pulsate"));
1793 obj->bypass = 0;
1798 * Some parts of this function expect the object to be on the floor
1799 * obj->{ox,oy} to be valid. The exception to this (so far) is
1800 * for the STONE_TO_FLESH spell.
1802 if (!(obj->where == OBJ_FLOOR || otmp->otyp == SPE_STONE_TO_FLESH))
1803 impossible("bhito: obj is not floor or Stone To Flesh spell");
1805 if (obj == uball) {
1806 res = 0;
1807 } else if (obj == uchain) {
1808 if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) {
1809 learn_it = TRUE;
1810 unpunish();
1811 } else
1812 res = 0;
1813 } else
1814 switch (otmp->otyp) {
1815 case WAN_POLYMORPH:
1816 case SPE_POLYMORPH:
1817 if (obj->otyp == WAN_POLYMORPH || obj->otyp == SPE_POLYMORPH
1818 || obj->otyp == POT_POLYMORPH || obj_resists(obj, 5, 95)) {
1819 res = 0;
1820 break;
1822 /* KMH, conduct */
1823 u.uconduct.polypiles++;
1824 /* any saved lock context will be dangerously obsolete */
1825 if (Is_box(obj))
1826 (void) boxlock(obj, otmp);
1828 if (obj_shudders(obj)) {
1829 boolean cover =
1830 ((obj == level.objects[u.ux][u.uy]) && u.uundetected
1831 && hides_under(youmonst.data));
1833 if (cansee(obj->ox, obj->oy))
1834 learn_it = TRUE;
1835 do_osshock(obj);
1836 /* eek - your cover might have been blown */
1837 if (cover)
1838 (void) hideunder(&youmonst);
1839 break;
1841 obj = poly_obj(obj, STRANGE_OBJECT);
1842 newsym(obj->ox, obj->oy);
1843 break;
1844 case WAN_PROBING:
1845 res = !obj->dknown;
1846 /* target object has now been "seen (up close)" */
1847 obj->dknown = 1;
1848 if (Is_container(obj) || obj->otyp == STATUE) {
1849 obj->cknown = obj->lknown = 1;
1850 if (!obj->cobj) {
1851 boolean catbox = SchroedingersBox(obj);
1853 /* we don't want to force alive vs dead
1854 determination for Schroedinger's Cat here,
1855 so just make probing be inconclusive for it */
1856 if (catbox)
1857 obj->cknown = 0;
1858 pline("%s empty.", Tobjnam(obj, catbox ? "seem" : "are"));
1859 } else {
1860 struct obj *o;
1861 /* view contents (not recursively) */
1862 for (o = obj->cobj; o; o = o->nobj)
1863 o->dknown = 1; /* "seen", even if blind */
1864 (void) display_cinventory(obj);
1866 res = 1;
1868 if (res)
1869 learn_it = TRUE;
1870 break;
1871 case WAN_STRIKING:
1872 case SPE_FORCE_BOLT:
1873 /* learn the type if you see or hear something break
1874 (the sound could be implicit) */
1875 maybelearnit = cansee(obj->ox, obj->oy) || !Deaf;
1876 if (obj->otyp == BOULDER) {
1877 if (cansee(obj->ox, obj->oy))
1878 pline_The("boulder falls apart.");
1879 else
1880 You_hear("a crumbling sound.");
1881 fracture_rock(obj);
1882 } else if (obj->otyp == STATUE) {
1883 if (break_statue(obj)) {
1884 if (cansee(obj->ox, obj->oy)) {
1885 if (Hallucination)
1886 pline_The("%s shatters.", rndmonnam(NULL));
1887 else
1888 pline_The("statue shatters.");
1889 } else
1890 You_hear("a crumbling sound.");
1892 } else {
1893 int oox = obj->ox;
1894 int ooy = obj->oy;
1895 if (context.mon_moving
1896 ? !breaks(obj, obj->ox, obj->oy)
1897 : !hero_breaks(obj, obj->ox, obj->oy, FALSE))
1898 maybelearnit = FALSE; /* nothing broke */
1899 else
1900 newsym_force(oox,ooy);
1901 res = 0;
1903 if (maybelearnit)
1904 learn_it = TRUE;
1905 break;
1906 case WAN_CANCELLATION:
1907 case SPE_CANCELLATION:
1908 cancel_item(obj);
1909 #ifdef TEXTCOLOR
1910 newsym(obj->ox, obj->oy); /* might change color */
1911 #endif
1912 break;
1913 case SPE_DRAIN_LIFE:
1914 (void) drain_item(obj);
1915 break;
1916 case WAN_TELEPORTATION:
1917 case SPE_TELEPORT_AWAY:
1918 (void) rloco(obj);
1919 break;
1920 case WAN_MAKE_INVISIBLE:
1921 break;
1922 case WAN_UNDEAD_TURNING:
1923 case SPE_TURN_UNDEAD:
1924 if (obj->otyp == EGG) {
1925 revive_egg(obj);
1926 } else if (obj->otyp == CORPSE) {
1927 int corpsenm = corpse_revive_type(obj);
1929 res = !!revive(obj, TRUE);
1930 if (res && Role_if(PM_HEALER)) {
1931 if (Hallucination && !Deaf) {
1932 You_hear("the sound of a defibrillator.");
1933 learn_it = TRUE;
1934 } else if (!Blind) {
1935 You("observe %s %s change dramatically.",
1936 s_suffix(an(mons[corpsenm].mname)),
1937 nonliving(&mons[corpsenm]) ? "motility"
1938 : "health");
1939 learn_it = TRUE;
1941 if (learn_it)
1942 exercise(A_WIS, TRUE);
1945 break;
1946 case WAN_OPENING:
1947 case SPE_KNOCK:
1948 case WAN_LOCKING:
1949 case SPE_WIZARD_LOCK:
1950 if (Is_box(obj))
1951 res = boxlock(obj, otmp);
1952 else
1953 res = 0;
1954 if (res)
1955 learn_it = TRUE;
1956 break;
1957 case WAN_SLOW_MONSTER: /* no effect on objects */
1958 case SPE_SLOW_MONSTER:
1959 case WAN_SPEED_MONSTER:
1960 case WAN_NOTHING:
1961 case SPE_HEALING:
1962 case SPE_EXTRA_HEALING:
1963 res = 0;
1964 break;
1965 case SPE_STONE_TO_FLESH:
1966 res = stone_to_flesh_obj(obj);
1967 break;
1968 default:
1969 impossible("What an interesting effect (%d)", otmp->otyp);
1970 break;
1972 /* if effect was observable then discover the wand type provided
1973 that the wand itself has been seen */
1974 if (learn_it)
1975 learnwand(otmp);
1976 return res;
1979 /* returns nonzero if something was hit */
1981 bhitpile(obj, fhito, tx, ty, zz)
1982 struct obj *obj;
1983 int FDECL((*fhito), (OBJ_P, OBJ_P));
1984 int tx, ty;
1985 schar zz;
1987 int hitanything = 0;
1988 register struct obj *otmp, *next_obj;
1990 if (obj->otyp == SPE_FORCE_BOLT || obj->otyp == WAN_STRIKING) {
1991 struct trap *t = t_at(tx, ty);
1993 /* We can't settle for the default calling sequence of
1994 bhito(otmp) -> break_statue(otmp) -> activate_statue_trap(ox,oy)
1995 because that last call might end up operating on our `next_obj'
1996 (below), rather than on the current object, if it happens to
1997 encounter a statue which mustn't become animated. */
1998 if (t && t->ttyp == STATUE_TRAP
1999 && activate_statue_trap(t, tx, ty, TRUE))
2000 learnwand(obj);
2003 poly_zapped = -1;
2004 for (otmp = level.objects[tx][ty]; otmp; otmp = next_obj) {
2005 next_obj = otmp->nexthere;
2006 /* for zap downwards, don't hit object poly'd hero is hiding under */
2007 if (zz > 0 && u.uundetected && otmp == level.objects[u.ux][u.uy]
2008 && hides_under(youmonst.data))
2009 continue;
2011 hitanything += (*fhito)(otmp, obj);
2013 if (poly_zapped >= 0)
2014 create_polymon(level.objects[tx][ty], poly_zapped);
2016 return hitanything;
2020 * zappable - returns 1 if zap is available, 0 otherwise.
2021 * it removes a charge from the wand if zappable.
2022 * added by GAN 11/03/86
2025 zappable(wand)
2026 register struct obj *wand;
2028 if (wand->spe < 0 || (wand->spe == 0 && rn2(121)))
2029 return 0;
2030 if (wand->spe == 0)
2031 You("wrest one last charge from the worn-out wand.");
2032 wand->spe--;
2033 return 1;
2037 * zapnodir - zaps a NODIR wand/spell.
2038 * added by GAN 11/03/86
2040 void
2041 zapnodir(obj)
2042 register struct obj *obj;
2044 boolean known = FALSE;
2046 switch (obj->otyp) {
2047 case WAN_LIGHT:
2048 case SPE_LIGHT:
2049 litroom(TRUE, obj);
2050 if (!Blind)
2051 known = TRUE;
2052 if (lightdamage(obj, TRUE, 5))
2053 known = TRUE;
2054 break;
2055 case WAN_SECRET_DOOR_DETECTION:
2056 case SPE_DETECT_UNSEEN:
2057 if (!findit())
2058 return;
2059 if (!Blind)
2060 known = TRUE;
2061 break;
2062 case WAN_CREATE_MONSTER:
2063 known = create_critters(rn2(23) ? 1 : rn1(7, 2),
2064 (struct permonst *) 0, FALSE);
2065 break;
2066 case WAN_WISHING:
2067 known = TRUE;
2068 if (Luck + rn2(5) < 0) {
2069 pline("Unfortunately, nothing happens.");
2070 break;
2072 makewish();
2073 break;
2074 case WAN_ENLIGHTENMENT:
2075 known = TRUE;
2076 You_feel("self-knowledgeable...");
2077 display_nhwindow(WIN_MESSAGE, FALSE);
2078 enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS);
2079 pline_The("feeling subsides.");
2080 exercise(A_WIS, TRUE);
2081 break;
2083 if (known) {
2084 if (!objects[obj->otyp].oc_name_known)
2085 more_experienced(0, 10);
2086 /* effect was observable; discover the wand type provided
2087 that the wand itself has been seen */
2088 learnwand(obj);
2092 STATIC_OVL void
2093 backfire(otmp)
2094 struct obj *otmp;
2096 int dmg;
2097 otmp->in_use = TRUE; /* in case losehp() is fatal */
2098 pline("%s suddenly explodes!", The(xname(otmp)));
2099 dmg = d(otmp->spe + 2, 6);
2100 losehp(Maybe_Half_Phys(dmg), "exploding wand", KILLED_BY_AN);
2101 useup(otmp);
2104 static NEARDATA const char zap_syms[] = { WAND_CLASS, 0 };
2106 /* 'z' command (or 'y' if numbed_pad==-1) */
2108 dozap()
2110 register struct obj *obj;
2111 int damage;
2113 if (check_capacity((char *) 0))
2114 return 0;
2115 obj = getobj(zap_syms, "zap");
2116 if (!obj)
2117 return 0;
2119 check_unpaid(obj);
2121 /* zappable addition done by GAN 11/03/86 */
2122 if (!zappable(obj))
2123 pline1(nothing_happens);
2124 else if (obj->cursed && !rn2(WAND_BACKFIRE_CHANCE)) {
2125 backfire(obj); /* the wand blows up in your face! */
2126 exercise(A_STR, FALSE);
2127 return 1;
2128 } else if (!(objects[obj->otyp].oc_dir == NODIR) && !getdir((char *) 0)) {
2129 if (!Blind)
2130 pline("%s glows and fades.", The(xname(obj)));
2131 /* make him pay for knowing !NODIR */
2132 } else if (!u.dx && !u.dy && !u.dz
2133 && !(objects[obj->otyp].oc_dir == NODIR)) {
2134 if ((damage = zapyourself(obj, TRUE)) != 0) {
2135 char buf[BUFSZ];
2137 Sprintf(buf, "zapped %sself with a wand", uhim());
2138 losehp(Maybe_Half_Phys(damage), buf, NO_KILLER_PREFIX);
2140 } else {
2141 /* Are we having fun yet?
2142 * weffects -> buzz(obj->otyp) -> zhitm (temple priest) ->
2143 * attack -> hitum -> known_hitum -> ghod_hitsu ->
2144 * buzz(AD_ELEC) -> destroy_item(WAND_CLASS) ->
2145 * useup -> obfree -> dealloc_obj -> free(obj)
2147 current_wand = obj;
2148 weffects(obj);
2149 obj = current_wand;
2150 current_wand = 0;
2152 if (obj && obj->spe < 0) {
2153 pline("%s to dust.", Tobjnam(obj, "turn"));
2154 useup(obj);
2156 update_inventory(); /* maybe used a charge */
2157 return 1;
2161 zapyourself(obj, ordinary)
2162 struct obj *obj;
2163 boolean ordinary;
2165 boolean learn_it = FALSE;
2166 int damage = 0;
2168 switch (obj->otyp) {
2169 case WAN_STRIKING:
2170 case SPE_FORCE_BOLT:
2171 learn_it = TRUE;
2172 if (Antimagic) {
2173 shieldeff(u.ux, u.uy);
2174 pline("Boing!");
2175 } else {
2176 if (ordinary) {
2177 You("bash yourself!");
2178 damage = d(2, 12);
2179 } else
2180 damage = d(1 + obj->spe, 6);
2181 exercise(A_STR, FALSE);
2183 break;
2185 case WAN_LIGHTNING:
2186 learn_it = TRUE;
2187 if (!Shock_resistance) {
2188 You("shock yourself!");
2189 damage = d(12, 6);
2190 exercise(A_CON, FALSE);
2191 } else {
2192 shieldeff(u.ux, u.uy);
2193 You("zap yourself, but seem unharmed.");
2194 ugolemeffects(AD_ELEC, d(12, 6));
2196 destroy_item(WAND_CLASS, AD_ELEC);
2197 destroy_item(RING_CLASS, AD_ELEC);
2198 (void) flashburn((long) rnd(100));
2199 break;
2201 case SPE_FIREBALL:
2202 You("explode a fireball on top of yourself!");
2203 explode(u.ux, u.uy, 11, d(6, 6), WAND_CLASS, EXPL_FIERY);
2204 break;
2205 case WAN_FIRE:
2206 case FIRE_HORN:
2207 learn_it = TRUE;
2208 if (Fire_resistance) {
2209 shieldeff(u.ux, u.uy);
2210 You_feel("rather warm.");
2211 ugolemeffects(AD_FIRE, d(12, 6));
2212 } else {
2213 pline("You've set yourself afire!");
2214 damage = d(12, 6);
2216 burn_away_slime();
2217 (void) burnarmor(&youmonst);
2218 destroy_item(SCROLL_CLASS, AD_FIRE);
2219 destroy_item(POTION_CLASS, AD_FIRE);
2220 destroy_item(SPBOOK_CLASS, AD_FIRE);
2221 destroy_item(FOOD_CLASS, AD_FIRE); /* only slime for now */
2222 break;
2224 case WAN_COLD:
2225 case SPE_CONE_OF_COLD:
2226 case FROST_HORN:
2227 learn_it = TRUE;
2228 if (Cold_resistance) {
2229 shieldeff(u.ux, u.uy);
2230 You_feel("a little chill.");
2231 ugolemeffects(AD_COLD, d(12, 6));
2232 } else {
2233 You("imitate a popsicle!");
2234 damage = d(12, 6);
2236 destroy_item(POTION_CLASS, AD_COLD);
2237 break;
2239 case WAN_MAGIC_MISSILE:
2240 case SPE_MAGIC_MISSILE:
2241 learn_it = TRUE;
2242 if (Antimagic) {
2243 shieldeff(u.ux, u.uy);
2244 pline_The("missiles bounce!");
2245 } else {
2246 damage = d(4, 6);
2247 pline("Idiot! You've shot yourself!");
2249 break;
2251 case WAN_POLYMORPH:
2252 case SPE_POLYMORPH:
2253 if (!Unchanging) {
2254 learn_it = TRUE;
2255 polyself(0);
2257 break;
2259 case WAN_CANCELLATION:
2260 case SPE_CANCELLATION:
2261 (void) cancel_monst(&youmonst, obj, TRUE, FALSE, TRUE);
2262 break;
2264 case SPE_DRAIN_LIFE:
2265 if (!Drain_resistance) {
2266 learn_it = TRUE; /* (no effect for spells...) */
2267 losexp("life drainage");
2269 damage = 0; /* No additional damage */
2270 break;
2272 case WAN_MAKE_INVISIBLE: {
2273 /* have to test before changing HInvis but must change
2274 * HInvis before doing newsym().
2276 int msg = !Invis && !Blind && !BInvis;
2278 if (BInvis && uarmc->otyp == MUMMY_WRAPPING) {
2279 /* A mummy wrapping absorbs it and protects you */
2280 You_feel("rather itchy under %s.", yname(uarmc));
2281 break;
2283 if (ordinary || !rn2(10)) { /* permanent */
2284 HInvis |= FROMOUTSIDE;
2285 } else { /* temporary */
2286 incr_itimeout(&HInvis, d(obj->spe, 250));
2288 if (msg) {
2289 learn_it = TRUE;
2290 newsym(u.ux, u.uy);
2291 self_invis_message();
2293 break;
2296 case WAN_SPEED_MONSTER:
2297 if (!(HFast & INTRINSIC)) {
2298 learn_it = TRUE;
2299 if (!Fast)
2300 You("speed up.");
2301 else
2302 Your("quickness feels more natural.");
2303 exercise(A_DEX, TRUE);
2305 HFast |= FROMOUTSIDE;
2306 break;
2308 case WAN_SLEEP:
2309 case SPE_SLEEP:
2310 learn_it = TRUE;
2311 if (Sleep_resistance) {
2312 shieldeff(u.ux, u.uy);
2313 You("don't feel sleepy!");
2314 } else {
2315 pline_The("sleep ray hits you!");
2316 fall_asleep(-rnd(50), TRUE);
2318 break;
2320 case WAN_SLOW_MONSTER:
2321 case SPE_SLOW_MONSTER:
2322 if (HFast & (TIMEOUT | INTRINSIC)) {
2323 learn_it = TRUE;
2324 u_slow_down();
2326 break;
2328 case WAN_TELEPORTATION:
2329 case SPE_TELEPORT_AWAY:
2330 tele();
2331 /* same criteria as when mounted (zap_steed) */
2332 if ((Teleport_control && !Stunned) || !couldsee(u.ux0, u.uy0)
2333 || distu(u.ux0, u.uy0) >= 16)
2334 learn_it = TRUE;
2335 break;
2337 case WAN_DEATH:
2338 case SPE_FINGER_OF_DEATH:
2339 if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
2340 pline((obj->otyp == WAN_DEATH)
2341 ? "The wand shoots an apparently harmless beam at you."
2342 : "You seem no deader than before.");
2343 break;
2345 learn_it = TRUE;
2346 Sprintf(killer.name, "shot %sself with a death ray", uhim());
2347 killer.format = NO_KILLER_PREFIX;
2348 You("irradiate yourself with pure energy!");
2349 You("die.");
2350 /* They might survive with an amulet of life saving */
2351 done(DIED);
2352 break;
2353 case WAN_UNDEAD_TURNING:
2354 case SPE_TURN_UNDEAD:
2355 learn_it = TRUE;
2356 (void) unturn_dead(&youmonst);
2357 if (is_undead(youmonst.data)) {
2358 You_feel("frightened and %sstunned.",
2359 Stunned ? "even more " : "");
2360 make_stunned((HStun & TIMEOUT) + (long) rnd(30), FALSE);
2361 } else
2362 You("shudder in dread.");
2363 break;
2364 case SPE_HEALING:
2365 case SPE_EXTRA_HEALING:
2366 learn_it = TRUE; /* (no effect for spells...) */
2367 healup(d(6, obj->otyp == SPE_EXTRA_HEALING ? 8 : 4), 0, FALSE,
2368 (obj->otyp == SPE_EXTRA_HEALING));
2369 You_feel("%sbetter.", obj->otyp == SPE_EXTRA_HEALING ? "much " : "");
2370 break;
2371 case WAN_LIGHT: /* (broken wand) */
2372 /* assert( !ordinary ); */
2373 damage = d(obj->spe, 25);
2374 case EXPENSIVE_CAMERA:
2375 if (!damage)
2376 damage = 5;
2377 damage = lightdamage(obj, ordinary, damage);
2378 damage += rnd(25);
2379 if (flashburn((long) damage))
2380 learn_it = TRUE;
2381 damage = 0; /* reset */
2382 break;
2383 case WAN_OPENING:
2384 case SPE_KNOCK:
2385 if (Punished) {
2386 learn_it = TRUE;
2387 unpunish();
2389 if (u.utrap) { /* escape web or bear trap */
2390 (void) openholdingtrap(&youmonst, &learn_it);
2391 } else {
2392 struct obj *otmp;
2393 /* unlock carried boxes */
2394 for (otmp = invent; otmp; otmp = otmp->nobj)
2395 if (Is_box(otmp))
2396 (void) boxlock(otmp, obj);
2397 /* trigger previously escaped trapdoor */
2398 (void) openfallingtrap(&youmonst, TRUE, &learn_it);
2400 break;
2401 case WAN_LOCKING:
2402 case SPE_WIZARD_LOCK:
2403 if (!u.utrap) {
2404 (void) closeholdingtrap(&youmonst, &learn_it);
2406 break;
2407 case WAN_DIGGING:
2408 case SPE_DIG:
2409 case SPE_DETECT_UNSEEN:
2410 case WAN_NOTHING:
2411 break;
2412 case WAN_PROBING: {
2413 struct obj *otmp;
2415 for (otmp = invent; otmp; otmp = otmp->nobj) {
2416 otmp->dknown = 1;
2417 if (Is_container(otmp) || otmp->otyp == STATUE) {
2418 otmp->lknown = 1;
2419 if (!SchroedingersBox(otmp))
2420 otmp->cknown = 1;
2423 learn_it = TRUE;
2424 ustatusline();
2425 break;
2427 case SPE_STONE_TO_FLESH: {
2428 struct obj *otmp, *onxt;
2429 boolean didmerge;
2431 if (u.umonnum == PM_STONE_GOLEM) {
2432 learn_it = TRUE;
2433 (void) polymon(PM_FLESH_GOLEM);
2435 if (Stoned) {
2436 learn_it = TRUE;
2437 fix_petrification(); /* saved! */
2439 /* but at a cost.. */
2440 for (otmp = invent; otmp; otmp = onxt) {
2441 onxt = otmp->nobj;
2442 if (bhito(otmp, obj))
2443 learn_it = TRUE;
2446 * It is possible that we can now merge some inventory.
2447 * Do a highly paranoid merge. Restart from the beginning
2448 * until no merges.
2450 do {
2451 didmerge = FALSE;
2452 for (otmp = invent; !didmerge && otmp; otmp = otmp->nobj)
2453 for (onxt = otmp->nobj; onxt; onxt = onxt->nobj)
2454 if (merged(&otmp, &onxt)) {
2455 didmerge = TRUE;
2456 break;
2458 } while (didmerge);
2459 break;
2461 default:
2462 impossible("zapyourself: object %d used?", obj->otyp);
2463 break;
2465 /* if effect was observable then discover the wand type provided
2466 that the wand itself has been seen */
2467 if (learn_it)
2468 learnwand(obj);
2469 return damage;
2472 /* called when poly'd hero uses breath attack against self */
2473 void
2474 ubreatheu(mattk)
2475 struct attack *mattk;
2477 int dtyp = 20 + mattk->adtyp - 1; /* breath by hero */
2478 const char *fltxt = flash_types[dtyp]; /* blast of <something> */
2480 zhitu(dtyp, mattk->damn, fltxt, u.ux, u.uy);
2483 /* light damages hero in gremlin form */
2485 lightdamage(obj, ordinary, amt)
2486 struct obj *obj; /* item making light (fake book if spell) */
2487 boolean ordinary; /* wand/camera zap vs wand destruction */
2488 int amt; /* pseudo-damage used to determine blindness duration */
2490 char buf[BUFSZ];
2491 const char *how;
2492 int dmg = amt;
2494 if (dmg && youmonst.data == &mons[PM_GREMLIN]) {
2495 /* reduce high values (from destruction of wand with many charges) */
2496 dmg = rnd(dmg);
2497 if (dmg > 10)
2498 dmg = 10 + rnd(dmg - 10);
2499 if (dmg > 20)
2500 dmg = 20;
2501 pline("Ow, that light hurts%c", (dmg > 2 || u.mh <= 5) ? '!' : '.');
2502 /* [composing killer/reason is superfluous here; if fatal, cause
2503 of death will always be "killed while stuck in creature form"] */
2504 if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS)
2505 ordinary = FALSE; /* say blasted rather than zapped */
2506 how = (obj->oclass != SPBOOK_CLASS)
2507 ? (const char *) ansimpleoname(obj)
2508 : "spell of light";
2509 Sprintf(buf, "%s %sself with %s", ordinary ? "zapped" : "blasted",
2510 uhim(), how);
2511 /* might rehumanize(); could be fatal, but only for Unchanging */
2512 losehp(Maybe_Half_Phys(dmg), buf, NO_KILLER_PREFIX);
2514 return dmg;
2517 /* light[ning] causes blindness */
2518 boolean
2519 flashburn(duration)
2520 long duration;
2522 if (!resists_blnd(&youmonst)) {
2523 You(are_blinded_by_the_flash);
2524 make_blinded(duration, FALSE);
2525 if (!Blind)
2526 Your1(vision_clears);
2527 return TRUE;
2529 return FALSE;
2532 /* you've zapped a wand downwards while riding
2533 * Return TRUE if the steed was hit by the wand.
2534 * Return FALSE if the steed was not hit by the wand.
2536 STATIC_OVL boolean
2537 zap_steed(obj)
2538 struct obj *obj; /* wand or spell */
2540 int steedhit = FALSE;
2542 bhitpos.x = u.usteed->mx, bhitpos.y = u.usteed->my;
2543 notonhead = FALSE;
2544 switch (obj->otyp) {
2546 * Wands that are allowed to hit the steed
2547 * Carefully test the results of any that are
2548 * moved here from the bottom section.
2550 case WAN_PROBING:
2551 probe_monster(u.usteed);
2552 learnwand(obj);
2553 steedhit = TRUE;
2554 break;
2555 case WAN_TELEPORTATION:
2556 case SPE_TELEPORT_AWAY:
2557 /* you go together */
2558 tele();
2559 /* same criteria as when unmounted (zapyourself) */
2560 if ((Teleport_control && !Stunned) || !couldsee(u.ux0, u.uy0)
2561 || distu(u.ux0, u.uy0) >= 16)
2562 learnwand(obj);
2563 steedhit = TRUE;
2564 break;
2566 /* Default processing via bhitm() for these */
2567 case SPE_CURE_SICKNESS:
2568 case WAN_MAKE_INVISIBLE:
2569 case WAN_CANCELLATION:
2570 case SPE_CANCELLATION:
2571 case WAN_POLYMORPH:
2572 case SPE_POLYMORPH:
2573 case WAN_STRIKING:
2574 case SPE_FORCE_BOLT:
2575 case WAN_SLOW_MONSTER:
2576 case SPE_SLOW_MONSTER:
2577 case WAN_SPEED_MONSTER:
2578 case SPE_HEALING:
2579 case SPE_EXTRA_HEALING:
2580 case SPE_DRAIN_LIFE:
2581 case WAN_OPENING:
2582 case SPE_KNOCK:
2583 (void) bhitm(u.usteed, obj);
2584 steedhit = TRUE;
2585 break;
2587 default:
2588 steedhit = FALSE;
2589 break;
2591 return steedhit;
2595 * cancel a monster (possibly the hero). inventory is cancelled only
2596 * if the monster is zapping itself directly, since otherwise the
2597 * effect is too strong. currently non-hero monsters do not zap
2598 * themselves with cancellation.
2600 boolean
2601 cancel_monst(mdef, obj, youattack, allow_cancel_kill, self_cancel)
2602 register struct monst *mdef;
2603 register struct obj *obj;
2604 boolean youattack, allow_cancel_kill, self_cancel;
2606 boolean youdefend = (mdef == &youmonst);
2607 static const char writing_vanishes[] =
2608 "Some writing vanishes from %s head!";
2609 static const char your[] = "your"; /* should be extern */
2611 if (youdefend ? (!youattack && Antimagic)
2612 : resist(mdef, obj->oclass, 0, NOTELL))
2613 return FALSE; /* resisted cancellation */
2615 if (self_cancel) { /* 1st cancel inventory */
2616 struct obj *otmp;
2618 for (otmp = (youdefend ? invent : mdef->minvent); otmp;
2619 otmp = otmp->nobj)
2620 cancel_item(otmp);
2621 if (youdefend) {
2622 context.botl = 1; /* potential AC change */
2623 find_ac();
2627 /* now handle special cases */
2628 if (youdefend) {
2629 if (Upolyd) {
2630 if ((u.umonnum == PM_CLAY_GOLEM) && !Blind)
2631 pline(writing_vanishes, your);
2633 if (Unchanging)
2634 Your("amulet grows hot for a moment, then cools.");
2635 else
2636 rehumanize();
2638 } else {
2639 mdef->mcan = TRUE;
2641 if (is_were(mdef->data) && mdef->data->mlet != S_HUMAN)
2642 were_change(mdef);
2644 if (mdef->data == &mons[PM_CLAY_GOLEM]) {
2645 if (canseemon(mdef))
2646 pline(writing_vanishes, s_suffix(mon_nam(mdef)));
2648 if (allow_cancel_kill) {
2649 if (youattack)
2650 killed(mdef);
2651 else
2652 monkilled(mdef, "", AD_SPEL);
2656 return TRUE;
2659 /* you've zapped an immediate type wand up or down */
2660 STATIC_OVL boolean
2661 zap_updown(obj)
2662 struct obj *obj; /* wand or spell */
2664 boolean striking = FALSE, disclose = FALSE;
2665 int x, y, xx, yy, ptmp;
2666 struct obj *otmp;
2667 struct engr *e;
2668 struct trap *ttmp;
2669 char buf[BUFSZ];
2671 /* some wands have special effects other than normal bhitpile */
2672 /* drawbridge might change <u.ux,u.uy> */
2673 x = xx = u.ux; /* <x,y> is zap location */
2674 y = yy = u.uy; /* <xx,yy> is drawbridge (portcullis) position */
2675 ttmp = t_at(x, y); /* trap if there is one */
2677 switch (obj->otyp) {
2678 case WAN_PROBING:
2679 ptmp = 0;
2680 if (u.dz < 0) {
2681 You("probe towards the %s.", ceiling(x, y));
2682 } else {
2683 ptmp += bhitpile(obj, bhito, x, y, u.dz);
2684 You("probe beneath the %s.", surface(x, y));
2685 ptmp += display_binventory(x, y, TRUE);
2687 if (!ptmp)
2688 Your("probe reveals nothing.");
2689 return TRUE; /* we've done our own bhitpile */
2690 case WAN_OPENING:
2691 case SPE_KNOCK:
2692 /* up or down, but at closed portcullis only */
2693 if (is_db_wall(x, y) && find_drawbridge(&xx, &yy)) {
2694 open_drawbridge(xx, yy);
2695 disclose = TRUE;
2696 } else if (u.dz > 0 && (x == xdnstair && y == ydnstair) &&
2697 /* can't use the stairs down to quest level 2 until
2698 leader "unlocks" them; give feedback if you try */
2699 on_level(&u.uz, &qstart_level) && !ok_to_quest()) {
2700 pline_The("stairs seem to ripple momentarily.");
2701 disclose = TRUE;
2703 /* down will release you from bear trap or web */
2704 if (u.dz > 0 && u.utrap) {
2705 (void) openholdingtrap(&youmonst, &disclose);
2706 /* down will trigger trapdoor, hole, or [spiked-] pit */
2707 } else if (u.dz > 0 && !u.utrap) {
2708 (void) openfallingtrap(&youmonst, FALSE, &disclose);
2710 break;
2711 case WAN_STRIKING:
2712 case SPE_FORCE_BOLT:
2713 striking = TRUE;
2714 /*FALLTHRU*/
2715 case WAN_LOCKING:
2716 case SPE_WIZARD_LOCK:
2717 /* down at open bridge or up or down at open portcullis */
2718 if (((levl[x][y].typ == DRAWBRIDGE_DOWN)
2719 ? (u.dz > 0)
2720 : (is_drawbridge_wall(x, y) >= 0 && !is_db_wall(x, y)))
2721 && find_drawbridge(&xx, &yy)) {
2722 if (!striking)
2723 close_drawbridge(xx, yy);
2724 else
2725 destroy_drawbridge(xx, yy);
2726 disclose = TRUE;
2727 } else if (striking && u.dz < 0 && rn2(3) && !Is_airlevel(&u.uz)
2728 && !Is_waterlevel(&u.uz) && !Underwater
2729 && !Is_qstart(&u.uz)) {
2730 int dmg;
2731 /* similar to zap_dig() */
2732 pline("A rock is dislodged from the %s and falls on your %s.",
2733 ceiling(x, y), body_part(HEAD));
2734 dmg = rnd((uarmh && is_metallic(uarmh)) ? 2 : 6);
2735 losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN);
2736 if ((otmp = mksobj_at(ROCK, x, y, FALSE, FALSE)) != 0) {
2737 (void) xname(otmp); /* set dknown, maybe bknown */
2738 stackobj(otmp);
2740 newsym(x, y);
2741 } else if (u.dz > 0 && ttmp) {
2742 if (!striking && closeholdingtrap(&youmonst, &disclose)) {
2743 ; /* now stuck in web or bear trap */
2744 } else if (striking && ttmp->ttyp == TRAPDOOR) {
2745 /* striking transforms trapdoor into hole */
2746 if (Blind && !ttmp->tseen) {
2747 pline("%s beneath you shatters.", Something);
2748 } else if (!ttmp->tseen) { /* => !Blind */
2749 pline("There's a trapdoor beneath you; it shatters.");
2750 } else {
2751 pline("The trapdoor beneath you shatters.");
2752 disclose = TRUE;
2754 ttmp->ttyp = HOLE;
2755 ttmp->tseen = 1;
2756 newsym(x, y);
2757 /* might fall down hole */
2758 dotrap(ttmp, 0);
2759 } else if (!striking && ttmp->ttyp == HOLE) {
2760 /* locking transforms hole into trapdoor */
2761 ttmp->ttyp = TRAPDOOR;
2762 if (Blind || !ttmp->tseen) {
2763 pline("Some %s swirls beneath you.",
2764 is_ice(x, y) ? "frost" : "dust");
2765 } else {
2766 ttmp->tseen = 1;
2767 newsym(x, y);
2768 pline("A trapdoor appears beneath you.");
2769 disclose = TRUE;
2771 /* hadn't fallen down hole; won't fall now */
2774 break;
2775 case SPE_STONE_TO_FLESH:
2776 if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) || Underwater
2777 || (Is_qstart(&u.uz) && u.dz < 0)) {
2778 pline1(nothing_happens);
2779 } else if (u.dz < 0) { /* we should do more... */
2780 pline("Blood drips on your %s.", body_part(FACE));
2781 } else if (u.dz > 0 && !OBJ_AT(u.ux, u.uy)) {
2783 Print this message only if there wasn't an engraving
2784 affected here. If water or ice, act like waterlevel case.
2786 e = engr_at(u.ux, u.uy);
2787 if (!(e && e->engr_type == ENGRAVE)) {
2788 if (is_pool(u.ux, u.uy) || is_ice(u.ux, u.uy))
2789 pline1(nothing_happens);
2790 else
2791 pline("Blood %ss %s your %s.",
2792 is_lava(u.ux, u.uy) ? "boil" : "pool",
2793 Levitation ? "beneath" : "at",
2794 makeplural(body_part(FOOT)));
2797 break;
2798 default:
2799 break;
2802 if (u.dz > 0) {
2803 /* zapping downward */
2804 (void) bhitpile(obj, bhito, x, y, u.dz);
2806 /* subset of engraving effects; none sets `disclose' */
2807 if ((e = engr_at(x, y)) != 0 && e->engr_type != HEADSTONE) {
2808 switch (obj->otyp) {
2809 case WAN_POLYMORPH:
2810 case SPE_POLYMORPH:
2811 del_engr(e);
2812 make_engr_at(x, y, random_engraving(buf), moves, (xchar) 0);
2813 break;
2814 case WAN_CANCELLATION:
2815 case SPE_CANCELLATION:
2816 case WAN_MAKE_INVISIBLE:
2817 del_engr(e);
2818 break;
2819 case WAN_TELEPORTATION:
2820 case SPE_TELEPORT_AWAY:
2821 rloc_engr(e);
2822 break;
2823 case SPE_STONE_TO_FLESH:
2824 if (e->engr_type == ENGRAVE) {
2825 /* only affects things in stone */
2826 pline_The(Hallucination
2827 ? "floor runs like butter!"
2828 : "edges on the floor get smoother.");
2829 wipe_engr_at(x, y, d(2, 4), TRUE);
2831 break;
2832 case WAN_STRIKING:
2833 case SPE_FORCE_BOLT:
2834 wipe_engr_at(x, y, d(2, 4), TRUE);
2835 break;
2836 default:
2837 break;
2840 } else if (u.dz < 0) {
2841 /* zapping upward */
2843 /* game flavor: if you're hiding under "something"
2844 * a zap upward should hit that "something".
2846 if (u.uundetected && hides_under(youmonst.data)) {
2847 int hitit = 0;
2848 otmp = level.objects[u.ux][u.uy];
2850 if (otmp)
2851 hitit = bhito(otmp, obj);
2852 if (hitit) {
2853 (void) hideunder(&youmonst);
2854 disclose = TRUE;
2859 return disclose;
2862 /* used by do_break_wand() was well as by weffects() */
2863 void
2864 zapsetup()
2866 obj_zapped = FALSE;
2869 void
2870 zapwrapup()
2872 /* if do_osshock() set obj_zapped while polying, give a message now */
2873 if (obj_zapped)
2874 You_feel("shuddering vibrations.");
2875 obj_zapped = FALSE;
2878 /* called for various wand and spell effects - M. Stephenson */
2879 void
2880 weffects(obj)
2881 struct obj *obj;
2883 int otyp = obj->otyp;
2884 boolean disclose = FALSE, was_unkn = !objects[otyp].oc_name_known;
2886 exercise(A_WIS, TRUE);
2887 if (u.usteed && (objects[otyp].oc_dir != NODIR) && !u.dx && !u.dy
2888 && (u.dz > 0) && zap_steed(obj)) {
2889 disclose = TRUE;
2890 } else if (objects[otyp].oc_dir == IMMEDIATE) {
2891 zapsetup(); /* reset obj_zapped */
2892 if (u.uswallow) {
2893 (void) bhitm(u.ustuck, obj);
2894 /* [how about `bhitpile(u.ustuck->minvent)' effect?] */
2895 } else if (u.dz) {
2896 disclose = zap_updown(obj);
2897 } else {
2898 (void) bhit(u.dx, u.dy, rn1(8, 6), ZAPPED_WAND, bhitm, bhito,
2899 &obj);
2901 zapwrapup(); /* give feedback for obj_zapped */
2903 } else if (objects[otyp].oc_dir == NODIR) {
2904 zapnodir(obj);
2906 } else {
2907 /* neither immediate nor directionless */
2909 if (otyp == WAN_DIGGING || otyp == SPE_DIG)
2910 zap_dig();
2911 else if (otyp >= SPE_MAGIC_MISSILE && otyp <= SPE_FINGER_OF_DEATH)
2912 buzz(otyp - SPE_MAGIC_MISSILE + 10, u.ulevel / 2 + 1, u.ux, u.uy,
2913 u.dx, u.dy);
2914 else if (otyp >= WAN_MAGIC_MISSILE && otyp <= WAN_LIGHTNING)
2915 buzz(otyp - WAN_MAGIC_MISSILE,
2916 (otyp == WAN_MAGIC_MISSILE) ? 2 : 6, u.ux, u.uy, u.dx, u.dy);
2917 else
2918 impossible("weffects: unexpected spell or wand");
2919 disclose = TRUE;
2921 if (disclose) {
2922 learnwand(obj);
2923 if (was_unkn)
2924 more_experienced(0, 10);
2926 return;
2929 /* augment damage for a spell dased on the hero's intelligence (and level) */
2931 spell_damage_bonus(dmg)
2932 int dmg; /* base amount to be adjusted by bonus or penalty */
2934 int intell = ACURR(A_INT);
2936 /* Punish low intelligence before low level else low intelligence
2937 gets punished only when high level */
2938 if (intell <= 9) {
2939 /* -3 penalty, but never reduce combined amount below 1
2940 (if dmg is 0 for some reason, we're careful to leave it there) */
2941 if (dmg > 1)
2942 dmg = (dmg <= 3) ? 1 : dmg - 3;
2943 } else if (intell <= 13 || u.ulevel < 5)
2944 ; /* no bonus or penalty; dmg remains same */
2945 else if (intell <= 18)
2946 dmg += 1;
2947 else if (intell <= 24 || u.ulevel < 14)
2948 dmg += 2;
2949 else
2950 dmg += 3; /* Int 25 */
2952 return dmg;
2956 * Generate the to hit bonus for a spell. Based on the hero's skill in
2957 * spell class and dexterity.
2959 STATIC_OVL int
2960 spell_hit_bonus(skill)
2961 int skill;
2963 int hit_bon = 0;
2964 int dex = ACURR(A_DEX);
2966 switch (P_SKILL(spell_skilltype(skill))) {
2967 case P_ISRESTRICTED:
2968 case P_UNSKILLED:
2969 hit_bon = -4;
2970 break;
2971 case P_BASIC:
2972 hit_bon = 0;
2973 break;
2974 case P_SKILLED:
2975 hit_bon = 2;
2976 break;
2977 case P_EXPERT:
2978 hit_bon = 3;
2979 break;
2982 if (dex < 4)
2983 hit_bon -= 3;
2984 else if (dex < 6)
2985 hit_bon -= 2;
2986 else if (dex < 8)
2987 hit_bon -= 1;
2988 else if (dex < 14)
2989 /* Will change when print stuff below removed */
2990 hit_bon -= 0;
2991 else
2992 /* Even increment for dextrous heroes (see weapon.c abon) */
2993 hit_bon += dex - 14;
2995 return hit_bon;
2998 const char *
2999 exclam(force)
3000 int force;
3002 /* force == 0 occurs e.g. with sleep ray */
3003 /* note that large force is usual with wands so that !! would
3004 require information about hand/weapon/wand */
3005 return (const char *) ((force < 0) ? "?" : (force <= 4) ? "." : "!");
3008 void
3009 hit(str, mtmp, force)
3010 const char *str;
3011 struct monst *mtmp;
3012 const char *force; /* usually either "." or "!" */
3014 if ((!cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp)
3015 && !(u.uswallow && mtmp == u.ustuck)) || !flags.verbose)
3016 pline("%s %s it.", The(str), vtense(str, "hit"));
3017 else
3018 pline("%s %s %s%s", The(str), vtense(str, "hit"),
3019 mon_nam(mtmp), force);
3022 void
3023 miss(str, mtmp)
3024 register const char *str;
3025 register struct monst *mtmp;
3027 pline(
3028 "%s %s %s.", The(str), vtense(str, "miss"),
3029 ((cansee(bhitpos.x, bhitpos.y) || canspotmon(mtmp)) && flags.verbose)
3030 ? mon_nam(mtmp)
3031 : "it");
3034 STATIC_OVL void
3035 skiprange(range, skipstart, skipend)
3036 int range, *skipstart, *skipend;
3038 int tr = (range / 4);
3039 int tmp = range - ((tr > 0) ? rnd(tr) : 0);
3040 *skipstart = tmp;
3041 *skipend = tmp - ((tmp / 4) * rnd(3));
3042 if (*skipend >= tmp)
3043 *skipend = tmp - 1;
3047 * Called for the following distance effects:
3048 * when a weapon is thrown (weapon == THROWN_WEAPON)
3049 * when an object is kicked (KICKED_WEAPON)
3050 * when an IMMEDIATE wand is zapped (ZAPPED_WAND)
3051 * when a light beam is flashed (FLASHED_LIGHT)
3052 * when a mirror is applied (INVIS_BEAM)
3053 * A thrown/kicked object falls down at end of its range or when a monster
3054 * is hit. The variable 'bhitpos' is set to the final position of the weapon
3055 * thrown/zapped. The ray of a wand may affect (by calling a provided
3056 * function) several objects and monsters on its path. The return value
3057 * is the monster hit (weapon != ZAPPED_WAND), or a null monster pointer.
3059 * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be
3060 * destroyed and *pobj set to NULL to indicate this.
3062 * Check !u.uswallow before calling bhit().
3063 * This function reveals the absence of a remembered invisible monster in
3064 * necessary cases (throwing or kicking weapons). The presence of a real
3065 * one is revealed for a weapon, but if not a weapon is left up to fhitm().
3067 struct monst *
3068 bhit(ddx, ddy, range, weapon, fhitm, fhito, pobj)
3069 register int ddx, ddy, range; /* direction and range */
3070 int weapon; /* see values in hack.h */
3071 int FDECL((*fhitm), (MONST_P, OBJ_P)), /* fns called when mon/obj hit */
3072 FDECL((*fhito), (OBJ_P, OBJ_P));
3073 struct obj **pobj; /* object tossed/used, set to NULL
3074 * if object is destroyed */
3076 struct monst *mtmp;
3077 struct obj *obj = *pobj;
3078 uchar typ;
3079 boolean shopdoor = FALSE, point_blank = TRUE;
3080 boolean in_skip = FALSE, allow_skip = FALSE;
3081 int skiprange_start = 0, skiprange_end = 0, skipcount = 0;
3083 if (weapon == KICKED_WEAPON) {
3084 /* object starts one square in front of player */
3085 bhitpos.x = u.ux + ddx;
3086 bhitpos.y = u.uy + ddy;
3087 range--;
3088 } else {
3089 bhitpos.x = u.ux;
3090 bhitpos.y = u.uy;
3093 if (weapon == THROWN_WEAPON && obj && obj->otyp == ROCK) {
3094 skiprange(range, &skiprange_start, &skiprange_end);
3095 allow_skip = !rn2(3);
3098 if (weapon == FLASHED_LIGHT) {
3099 tmp_at(DISP_BEAM, cmap_to_glyph(S_flashbeam));
3100 } else if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
3101 tmp_at(DISP_FLASH, obj_to_glyph(obj));
3103 while (range-- > 0) {
3104 int x, y;
3106 bhitpos.x += ddx;
3107 bhitpos.y += ddy;
3108 x = bhitpos.x;
3109 y = bhitpos.y;
3111 if (!isok(x, y)) {
3112 bhitpos.x -= ddx;
3113 bhitpos.y -= ddy;
3114 break;
3117 if (is_pick(obj) && inside_shop(x, y)
3118 && (mtmp = shkcatch(obj, x, y)) != 0) {
3119 tmp_at(DISP_END, 0);
3120 return mtmp;
3123 typ = levl[bhitpos.x][bhitpos.y].typ;
3125 /* iron bars will block anything big enough */
3126 if ((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
3127 && typ == IRONBARS
3128 && hits_bars(pobj, x - ddx, y - ddy, bhitpos.x, bhitpos.y,
3129 point_blank ? 0 : !rn2(5), 1)) {
3130 /* caveat: obj might now be null... */
3131 obj = *pobj;
3132 bhitpos.x -= ddx;
3133 bhitpos.y -= ddy;
3134 break;
3137 if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) {
3138 boolean learn_it = FALSE;
3140 switch (obj->otyp) {
3141 case WAN_OPENING:
3142 case SPE_KNOCK:
3143 if (is_db_wall(bhitpos.x, bhitpos.y)) {
3144 if (cansee(x, y) || cansee(bhitpos.x, bhitpos.y))
3145 learn_it = TRUE;
3146 open_drawbridge(x, y);
3148 break;
3149 case WAN_LOCKING:
3150 case SPE_WIZARD_LOCK:
3151 if ((cansee(x, y) || cansee(bhitpos.x, bhitpos.y))
3152 && levl[x][y].typ == DRAWBRIDGE_DOWN)
3153 learn_it = TRUE;
3154 close_drawbridge(x, y);
3155 break;
3156 case WAN_STRIKING:
3157 case SPE_FORCE_BOLT:
3158 if (typ != DRAWBRIDGE_UP)
3159 destroy_drawbridge(x, y);
3160 learn_it = TRUE;
3161 break;
3163 if (learn_it)
3164 learnwand(obj);
3167 mtmp = m_at(bhitpos.x, bhitpos.y);
3170 * skipping rocks
3172 * skiprange_start is only set if this is a thrown rock
3174 if (skiprange_start && (range == skiprange_start) && allow_skip) {
3175 if (is_pool(bhitpos.x, bhitpos.y) && !mtmp) {
3176 in_skip = TRUE;
3177 if (!Blind)
3178 pline("%s %s%s.", Yname2(obj), otense(obj, "skip"),
3179 skipcount ? " again" : "");
3180 else
3181 You_hear("%s skip.", yname(obj));
3182 skipcount++;
3183 } else if (skiprange_start > skiprange_end + 1) {
3184 --skiprange_start;
3187 if (in_skip) {
3188 if (range <= skiprange_end) {
3189 in_skip = FALSE;
3190 if (range > 3) /* another bounce? */
3191 skiprange(range, &skiprange_start, &skiprange_end);
3192 } else if (mtmp && M_IN_WATER(mtmp->data)) {
3193 if ((!Blind && canseemon(mtmp)) || sensemon(mtmp))
3194 pline("%s %s over %s.", Yname2(obj), otense(obj, "pass"),
3195 mon_nam(mtmp));
3199 if (mtmp && !(in_skip && M_IN_WATER(mtmp->data))) {
3200 notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
3201 if (weapon == FLASHED_LIGHT) {
3202 /* FLASHED_LIGHT hitting invisible monster should
3203 pass through instead of stop so we call
3204 flash_hits_mon() directly rather than returning
3205 mtmp back to caller. That allows the flash to
3206 keep on going. Note that we use mtmp->minvis
3207 not canspotmon() because it makes no difference
3208 whether the hero can see the monster or not. */
3209 if (mtmp->minvis) {
3210 obj->ox = u.ux, obj->oy = u.uy;
3211 (void) flash_hits_mon(mtmp, obj);
3212 } else {
3213 tmp_at(DISP_END, 0);
3214 return mtmp; /* caller will call flash_hits_mon */
3216 } else if (weapon == INVIS_BEAM) {
3217 /* Like FLASHED_LIGHT, INVIS_BEAM should continue
3218 through invisible targets; unlike it, we aren't
3219 prepared for multiple hits so just get first one
3220 that's either visible or could see its invisible
3221 self. [No tmp_at() cleanup is needed here.] */
3222 if (!mtmp->minvis || perceives(mtmp->data))
3223 return mtmp;
3224 } else if (weapon != ZAPPED_WAND) {
3225 /* THROWN_WEAPON, KICKED_WEAPON */
3226 tmp_at(DISP_END, 0);
3227 if (cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp))
3228 map_invisible(bhitpos.x, bhitpos.y);
3229 return mtmp;
3230 } else {
3231 /* ZAPPED_WAND */
3232 (*fhitm)(mtmp, obj);
3233 range -= 3;
3235 } else {
3236 if (weapon == ZAPPED_WAND && obj->otyp == WAN_PROBING
3237 && glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)) {
3238 unmap_object(bhitpos.x, bhitpos.y);
3239 newsym(x, y);
3242 if (fhito) {
3243 if (bhitpile(obj, fhito, bhitpos.x, bhitpos.y, 0))
3244 range--;
3245 } else {
3246 if (weapon == KICKED_WEAPON
3247 && ((obj->oclass == COIN_CLASS
3248 && OBJ_AT(bhitpos.x, bhitpos.y))
3249 || ship_object(obj, bhitpos.x, bhitpos.y,
3250 costly_spot(bhitpos.x, bhitpos.y)))) {
3251 tmp_at(DISP_END, 0);
3252 return (struct monst *) 0;
3255 if (weapon == ZAPPED_WAND && (IS_DOOR(typ) || typ == SDOOR)) {
3256 switch (obj->otyp) {
3257 case WAN_OPENING:
3258 case WAN_LOCKING:
3259 case WAN_STRIKING:
3260 case SPE_KNOCK:
3261 case SPE_WIZARD_LOCK:
3262 case SPE_FORCE_BOLT:
3263 if (doorlock(obj, bhitpos.x, bhitpos.y)) {
3264 if (cansee(bhitpos.x, bhitpos.y)
3265 || (obj->otyp == WAN_STRIKING && !Deaf))
3266 learnwand(obj);
3267 if (levl[bhitpos.x][bhitpos.y].doormask == D_BROKEN
3268 && *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE)) {
3269 shopdoor = TRUE;
3270 add_damage(bhitpos.x, bhitpos.y, 400L);
3273 break;
3276 if (!ZAP_POS(typ) || closed_door(bhitpos.x, bhitpos.y)) {
3277 bhitpos.x -= ddx;
3278 bhitpos.y -= ddy;
3279 break;
3281 if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) {
3282 /* 'I' present but no monster: erase */
3283 /* do this before the tmp_at() */
3284 if (glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)
3285 && cansee(x, y)) {
3286 unmap_object(bhitpos.x, bhitpos.y);
3287 newsym(x, y);
3289 tmp_at(bhitpos.x, bhitpos.y);
3290 delay_output();
3291 /* kicked objects fall in pools */
3292 if ((weapon == KICKED_WEAPON)
3293 && (is_pool(bhitpos.x, bhitpos.y)
3294 || is_lava(bhitpos.x, bhitpos.y)))
3295 break;
3296 if (IS_SINK(typ) && weapon != FLASHED_LIGHT)
3297 break; /* physical objects fall onto sink */
3299 /* limit range of ball so hero won't make an invalid move */
3300 if (weapon == THROWN_WEAPON && range > 0
3301 && obj->otyp == HEAVY_IRON_BALL) {
3302 struct obj *bobj;
3303 struct trap *t;
3305 if ((bobj = sobj_at(BOULDER, x, y)) != 0) {
3306 if (cansee(x, y))
3307 pline("%s hits %s.", The(distant_name(obj, xname)),
3308 an(xname(bobj)));
3309 range = 0;
3310 } else if (obj == uball) {
3311 if (!test_move(x - ddx, y - ddy, ddx, ddy, TEST_MOVE)) {
3312 /* nb: it didn't hit anything directly */
3313 if (cansee(x, y))
3314 pline("%s jerks to an abrupt halt.",
3315 The(distant_name(obj, xname))); /* lame */
3316 range = 0;
3317 } else if (Sokoban && (t = t_at(x, y)) != 0
3318 && (t->ttyp == PIT || t->ttyp == SPIKED_PIT
3319 || t->ttyp == HOLE || t->ttyp == TRAPDOOR)) {
3320 /* hero falls into the trap, so ball stops */
3321 range = 0;
3326 /* thrown/kicked missile has moved away from its starting spot */
3327 point_blank = FALSE; /* affects passing through iron bars */
3330 if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
3331 tmp_at(DISP_END, 0);
3333 if (shopdoor)
3334 pay_for_damage("destroy", FALSE);
3336 return (struct monst *) 0;
3339 /* process thrown boomerang, which travels a curving path...
3340 * A multi-shot volley ought to have all missiles in flight at once,
3341 * but we're called separately for each one. We terminate the volley
3342 * early on a failed catch since continuing to throw after being hit
3343 * is too obviously silly.
3345 struct monst *
3346 boomhit(obj, dx, dy)
3347 struct obj *obj;
3348 int dx, dy;
3350 register int i, ct;
3351 int boom; /* showsym[] index */
3352 struct monst *mtmp;
3353 boolean counterclockwise = TRUE; /* right-handed throw */
3355 /* counterclockwise traversal patterns:
3356 * ..........................54.................................
3357 * ..................43.....6..3....765.........................
3358 * ..........32.....5..2...7...2...8...4....87..................
3359 * .........4..1....6..1...8..1....9...3...9..6.....98..........
3360 * ..21@....5...@...7..@....9@......@12....@...5...@..7.....@9..
3361 * .3...9....6..9....89.....................1..4...1..6....1..8.
3362 * .4...8.....78.............................23....2..5...2...7.
3363 * ..567............................................34....3..6..
3364 * ........................................................45...
3365 * (invert rows for corresponding clockwise patterns)
3368 bhitpos.x = u.ux;
3369 bhitpos.y = u.uy;
3370 boom = counterclockwise ? S_boomleft : S_boomright;
3371 for (i = 0; i < 8; i++)
3372 if (xdir[i] == dx && ydir[i] == dy)
3373 break;
3374 tmp_at(DISP_FLASH, cmap_to_glyph(boom));
3375 for (ct = 0; ct < 10; ct++) {
3376 i = (i + 8) % 8; /* 0..7 (8 -> 0, -1 -> 7) */
3377 boom = (S_boomleft + S_boomright - boom); /* toggle */
3378 tmp_at(DISP_CHANGE, cmap_to_glyph(boom)); /* change glyph */
3379 dx = xdir[i];
3380 dy = ydir[i];
3381 bhitpos.x += dx;
3382 bhitpos.y += dy;
3383 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
3384 m_respond(mtmp);
3385 tmp_at(DISP_END, 0);
3386 return mtmp;
3388 if (!ZAP_POS(levl[bhitpos.x][bhitpos.y].typ)
3389 || closed_door(bhitpos.x, bhitpos.y)) {
3390 bhitpos.x -= dx;
3391 bhitpos.y -= dy;
3392 break;
3394 if (bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */
3395 if (Fumbling || rn2(20) >= ACURR(A_DEX)) {
3396 /* we hit ourselves */
3397 (void) thitu(10 + obj->spe, dmgval(obj, &youmonst), obj,
3398 "boomerang");
3399 endmultishot(TRUE);
3400 break;
3401 } else { /* we catch it */
3402 tmp_at(DISP_END, 0);
3403 You("skillfully catch the boomerang.");
3404 return &youmonst;
3407 tmp_at(bhitpos.x, bhitpos.y);
3408 delay_output();
3409 if (IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) {
3410 if (!Deaf)
3411 pline("Klonk!");
3412 break; /* boomerang falls on sink */
3414 /* ct==0, initial position, we want next delta to be same;
3415 ct==5, opposite position, repeat delta undoes first one */
3416 if (ct % 5 != 0)
3417 i += (counterclockwise ? -1 : 1);
3419 tmp_at(DISP_END, 0); /* do not leave last symbol */
3420 return (struct monst *) 0;
3423 /* used by buzz(); also used by munslime(muse.c); returns damage to mon */
3425 zhitm(mon, type, nd, ootmp)
3426 register struct monst *mon;
3427 register int type, nd;
3428 struct obj **ootmp; /* to return worn armor for caller to disintegrate */
3430 register int tmp = 0;
3431 register int abstype = abs(type) % 10;
3432 boolean sho_shieldeff = FALSE;
3433 boolean spellcaster = is_hero_spell(type); /* maybe get a bonus! */
3435 *ootmp = (struct obj *) 0;
3436 switch (abstype) {
3437 case ZT_MAGIC_MISSILE:
3438 if (resists_magm(mon)) {
3439 sho_shieldeff = TRUE;
3440 break;
3442 tmp = d(nd, 6);
3443 if (spellcaster)
3444 tmp = spell_damage_bonus(tmp);
3445 break;
3446 case ZT_FIRE:
3447 if (resists_fire(mon)) {
3448 sho_shieldeff = TRUE;
3449 break;
3451 tmp = d(nd, 6);
3452 if (resists_cold(mon))
3453 tmp += 7;
3454 if (spellcaster)
3455 tmp = spell_damage_bonus(tmp);
3456 if (burnarmor(mon)) {
3457 if (!rn2(3))
3458 (void) destroy_mitem(mon, POTION_CLASS, AD_FIRE);
3459 if (!rn2(3))
3460 (void) destroy_mitem(mon, SCROLL_CLASS, AD_FIRE);
3461 if (!rn2(5))
3462 (void) destroy_mitem(mon, SPBOOK_CLASS, AD_FIRE);
3463 destroy_mitem(mon, FOOD_CLASS, AD_FIRE); /* carried slime */
3465 break;
3466 case ZT_COLD:
3467 if (resists_cold(mon)) {
3468 sho_shieldeff = TRUE;
3469 break;
3471 tmp = d(nd, 6);
3472 if (resists_fire(mon))
3473 tmp += d(nd, 3);
3474 if (spellcaster)
3475 tmp = spell_damage_bonus(tmp);
3476 if (!rn2(3))
3477 (void) destroy_mitem(mon, POTION_CLASS, AD_COLD);
3478 break;
3479 case ZT_SLEEP:
3480 tmp = 0;
3481 (void) sleep_monst(mon, d(nd, 25),
3482 type == ZT_WAND(ZT_SLEEP) ? WAND_CLASS : '\0');
3483 break;
3484 case ZT_DEATH: /* death/disintegration */
3485 if (abs(type) != ZT_BREATH(ZT_DEATH)) { /* death */
3486 if (mon->data == &mons[PM_DEATH]) {
3487 mon->mhpmax += mon->mhpmax / 2;
3488 if (mon->mhpmax >= MAGIC_COOKIE)
3489 mon->mhpmax = MAGIC_COOKIE - 1;
3490 mon->mhp = mon->mhpmax;
3491 tmp = 0;
3492 break;
3494 if (nonliving(mon->data) || is_demon(mon->data)
3495 || is_vampshifter(mon) || resists_magm(mon)) {
3496 /* similar to player */
3497 sho_shieldeff = TRUE;
3498 break;
3500 type = -1; /* so they don't get saving throws */
3501 } else {
3502 struct obj *otmp2;
3504 if (resists_disint(mon)) {
3505 sho_shieldeff = TRUE;
3506 } else if (mon->misc_worn_check & W_ARMS) {
3507 /* destroy shield; victim survives */
3508 *ootmp = which_armor(mon, W_ARMS);
3509 } else if (mon->misc_worn_check & W_ARM) {
3510 /* destroy body armor, also cloak if present */
3511 *ootmp = which_armor(mon, W_ARM);
3512 if ((otmp2 = which_armor(mon, W_ARMC)) != 0)
3513 m_useup(mon, otmp2);
3514 } else {
3515 /* no body armor, victim dies; destroy cloak
3516 and shirt now in case target gets life-saved */
3517 tmp = MAGIC_COOKIE;
3518 if ((otmp2 = which_armor(mon, W_ARMC)) != 0)
3519 m_useup(mon, otmp2);
3520 if ((otmp2 = which_armor(mon, W_ARMU)) != 0)
3521 m_useup(mon, otmp2);
3523 type = -1; /* no saving throw wanted */
3524 break; /* not ordinary damage */
3526 tmp = mon->mhp + 1;
3527 break;
3528 case ZT_LIGHTNING:
3529 if (resists_elec(mon)) {
3530 sho_shieldeff = TRUE;
3531 tmp = 0;
3532 /* can still blind the monster */
3533 } else
3534 tmp = d(nd, 6);
3535 if (spellcaster)
3536 tmp = spell_damage_bonus(tmp);
3537 if (!resists_blnd(mon)
3538 && !(type > 0 && u.uswallow && mon == u.ustuck)) {
3539 register unsigned rnd_tmp = rnd(50);
3540 mon->mcansee = 0;
3541 if ((mon->mblinded + rnd_tmp) > 127)
3542 mon->mblinded = 127;
3543 else
3544 mon->mblinded += rnd_tmp;
3546 if (!rn2(3))
3547 (void) destroy_mitem(mon, WAND_CLASS, AD_ELEC);
3548 /* not actually possible yet */
3549 if (!rn2(3))
3550 (void) destroy_mitem(mon, RING_CLASS, AD_ELEC);
3551 break;
3552 case ZT_POISON_GAS:
3553 if (resists_poison(mon)) {
3554 sho_shieldeff = TRUE;
3555 break;
3557 tmp = d(nd, 6);
3558 break;
3559 case ZT_ACID:
3560 if (resists_acid(mon)) {
3561 sho_shieldeff = TRUE;
3562 break;
3564 tmp = d(nd, 6);
3565 if (!rn2(6))
3566 acid_damage(MON_WEP(mon));
3567 if (!rn2(6))
3568 erode_armor(mon, ERODE_CORRODE);
3569 break;
3571 if (sho_shieldeff)
3572 shieldeff(mon->mx, mon->my);
3573 if (is_hero_spell(type) && (Role_if(PM_KNIGHT) && u.uhave.questart))
3574 tmp *= 2;
3575 if (tmp > 0 && type >= 0
3576 && resist(mon, type < ZT_SPELL(0) ? WAND_CLASS : '\0', 0, NOTELL))
3577 tmp /= 2;
3578 if (tmp < 0)
3579 tmp = 0; /* don't allow negative damage */
3580 debugpline3("zapped monster hp = %d (= %d - %d)", mon->mhp - tmp,
3581 mon->mhp, tmp);
3582 mon->mhp -= tmp;
3583 return tmp;
3586 STATIC_OVL void
3587 zhitu(type, nd, fltxt, sx, sy)
3588 int type, nd;
3589 const char *fltxt;
3590 xchar sx, sy;
3592 int dam = 0, abstyp = abs(type);
3594 switch (abstyp % 10) {
3595 case ZT_MAGIC_MISSILE:
3596 if (Antimagic) {
3597 shieldeff(sx, sy);
3598 pline_The("missiles bounce off!");
3599 } else {
3600 dam = d(nd, 6);
3601 exercise(A_STR, FALSE);
3603 break;
3604 case ZT_FIRE:
3605 if (Fire_resistance) {
3606 shieldeff(sx, sy);
3607 You("don't feel hot!");
3608 ugolemeffects(AD_FIRE, d(nd, 6));
3609 } else {
3610 dam = d(nd, 6);
3612 burn_away_slime();
3613 if (burnarmor(&youmonst)) { /* "body hit" */
3614 if (!rn2(3))
3615 destroy_item(POTION_CLASS, AD_FIRE);
3616 if (!rn2(3))
3617 destroy_item(SCROLL_CLASS, AD_FIRE);
3618 if (!rn2(5))
3619 destroy_item(SPBOOK_CLASS, AD_FIRE);
3620 destroy_item(FOOD_CLASS, AD_FIRE);
3622 break;
3623 case ZT_COLD:
3624 if (Cold_resistance) {
3625 shieldeff(sx, sy);
3626 You("don't feel cold.");
3627 ugolemeffects(AD_COLD, d(nd, 6));
3628 } else {
3629 dam = d(nd, 6);
3631 if (!rn2(3))
3632 destroy_item(POTION_CLASS, AD_COLD);
3633 break;
3634 case ZT_SLEEP:
3635 if (Sleep_resistance) {
3636 shieldeff(u.ux, u.uy);
3637 You("don't feel sleepy.");
3638 } else {
3639 fall_asleep(-d(nd, 25), TRUE); /* sleep ray */
3641 break;
3642 case ZT_DEATH:
3643 if (abstyp == ZT_BREATH(ZT_DEATH)) {
3644 if (Disint_resistance) {
3645 You("are not disintegrated.");
3646 break;
3647 } else if (uarms) {
3648 /* destroy shield; other possessions are safe */
3649 (void) destroy_arm(uarms);
3650 break;
3651 } else if (uarm) {
3652 /* destroy suit; if present, cloak goes too */
3653 if (uarmc)
3654 (void) destroy_arm(uarmc);
3655 (void) destroy_arm(uarm);
3656 break;
3658 /* no shield or suit, you're dead; wipe out cloak
3659 and/or shirt in case of life-saving or bones */
3660 if (uarmc)
3661 (void) destroy_arm(uarmc);
3662 if (uarmu)
3663 (void) destroy_arm(uarmu);
3664 } else if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
3665 shieldeff(sx, sy);
3666 You("seem unaffected.");
3667 break;
3668 } else if (Antimagic) {
3669 shieldeff(sx, sy);
3670 You("aren't affected.");
3671 break;
3673 killer.format = KILLED_BY_AN;
3674 Strcpy(killer.name, fltxt ? fltxt : "");
3675 /* when killed by disintegration breath, don't leave corpse */
3676 u.ugrave_arise = (type == -ZT_BREATH(ZT_DEATH)) ? -3 : NON_PM;
3677 done(DIED);
3678 return; /* lifesaved */
3679 case ZT_LIGHTNING:
3680 if (Shock_resistance) {
3681 shieldeff(sx, sy);
3682 You("aren't affected.");
3683 ugolemeffects(AD_ELEC, d(nd, 6));
3684 } else {
3685 dam = d(nd, 6);
3686 exercise(A_CON, FALSE);
3688 if (!rn2(3))
3689 destroy_item(WAND_CLASS, AD_ELEC);
3690 if (!rn2(3))
3691 destroy_item(RING_CLASS, AD_ELEC);
3692 break;
3693 case ZT_POISON_GAS:
3694 poisoned("blast", A_DEX, "poisoned blast", 15, FALSE);
3695 break;
3696 case ZT_ACID:
3697 if (Acid_resistance) {
3698 pline_The("acid doesn't hurt.");
3699 dam = 0;
3700 } else {
3701 pline_The("acid burns!");
3702 dam = d(nd, 6);
3703 exercise(A_STR, FALSE);
3705 /* using two weapons at once makes both of them more vulnerable */
3706 if (!rn2(u.twoweap ? 3 : 6))
3707 acid_damage(uwep);
3708 if (u.twoweap && !rn2(3))
3709 acid_damage(uswapwep);
3710 if (!rn2(6))
3711 erode_armor(&youmonst, ERODE_CORRODE);
3712 break;
3715 /* Half_spell_damage protection yields half-damage for wands & spells,
3716 including hero's own ricochets; breath attacks do full damage */
3717 if (dam && Half_spell_damage && !(abstyp >= 20 && abstyp <= 29))
3718 dam = (dam + 1) / 2;
3719 losehp(dam, fltxt, KILLED_BY_AN);
3720 return;
3724 * burn objects (such as scrolls and spellbooks) on floor
3725 * at position x,y; return the number of objects burned
3728 burn_floor_objects(x, y, give_feedback, u_caused)
3729 int x, y;
3730 boolean give_feedback; /* caller needs to decide about visibility checks */
3731 boolean u_caused;
3733 struct obj *obj, *obj2;
3734 long i, scrquan, delquan;
3735 char buf1[BUFSZ], buf2[BUFSZ];
3736 int cnt = 0;
3738 for (obj = level.objects[x][y]; obj; obj = obj2) {
3739 obj2 = obj->nexthere;
3740 if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS
3741 || (obj->oclass == FOOD_CLASS
3742 && obj->otyp == GLOB_OF_GREEN_SLIME)) {
3743 if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL
3744 || obj_resists(obj, 2, 100))
3745 continue;
3746 scrquan = obj->quan; /* number present */
3747 delquan = 0L; /* number to destroy */
3748 for (i = scrquan; i > 0L; i--)
3749 if (!rn2(3))
3750 delquan++;
3751 if (delquan) {
3752 /* save name before potential delobj() */
3753 if (give_feedback) {
3754 obj->quan = 1L;
3755 Strcpy(buf1, (x == u.ux && y == u.uy)
3756 ? xname(obj)
3757 : distant_name(obj, xname));
3758 obj->quan = 2L;
3759 Strcpy(buf2, (x == u.ux && y == u.uy)
3760 ? xname(obj)
3761 : distant_name(obj, xname));
3762 obj->quan = scrquan;
3764 /* useupf(), which charges, only if hero caused damage */
3765 if (u_caused)
3766 useupf(obj, delquan);
3767 else if (delquan < scrquan)
3768 obj->quan -= delquan;
3769 else
3770 delobj(obj);
3771 cnt += delquan;
3772 if (give_feedback) {
3773 if (delquan > 1L)
3774 pline("%ld %s burn.", delquan, buf2);
3775 else
3776 pline("%s burns.", An(buf1));
3781 return cnt;
3784 /* will zap/spell/breath attack score a hit against armor class `ac'? */
3785 STATIC_OVL int
3786 zap_hit(ac, type)
3787 int ac;
3788 int type; /* either hero cast spell type or 0 */
3790 int chance = rn2(20);
3791 int spell_bonus = type ? spell_hit_bonus(type) : 0;
3793 /* small chance for naked target to avoid being hit */
3794 if (!chance)
3795 return rnd(10) < ac + spell_bonus;
3797 /* very high armor protection does not achieve invulnerability */
3798 ac = AC_VALUE(ac);
3800 return (3 - chance < ac + spell_bonus);
3803 STATIC_OVL void
3804 disintegrate_mon(mon, type, fltxt)
3805 struct monst *mon;
3806 int type; /* hero vs other */
3807 const char *fltxt;
3809 struct obj *otmp, *otmp2, *m_amulet = mlifesaver(mon);
3811 if (canseemon(mon)) {
3812 if (!m_amulet)
3813 pline("%s is disintegrated!", Monnam(mon));
3814 else
3815 hit(fltxt, mon, "!");
3818 /* note: worn amulet of life saving must be preserved in order to operate */
3819 #define oresist_disintegration(obj) \
3820 (objects[obj->otyp].oc_oprop == DISINT_RES || obj_resists(obj, 5, 50) \
3821 || is_quest_artifact(obj) || obj == m_amulet)
3823 for (otmp = mon->minvent; otmp; otmp = otmp2) {
3824 otmp2 = otmp->nobj;
3825 if (!oresist_disintegration(otmp)) {
3826 if (otmp->owornmask) {
3827 /* in case monster's life gets saved */
3828 mon->misc_worn_check &= ~otmp->owornmask;
3829 if (otmp->owornmask & W_WEP)
3830 setmnotwielded(mon, otmp);
3831 /* also dismounts hero if this object is steed's saddle */
3832 update_mon_intrinsics(mon, otmp, FALSE, TRUE);
3833 otmp->owornmask = 0L;
3835 obj_extract_self(otmp);
3836 obfree(otmp, (struct obj *) 0);
3840 #undef oresist_disintegration
3842 if (type < 0)
3843 monkilled(mon, (char *) 0, -AD_RBRE);
3844 else
3845 xkilled(mon, 2);
3849 * type == 0 to 9 : you shooting a wand
3850 * type == 10 to 19 : you casting a spell
3851 * type == 20 to 29 : you breathing as a monster
3852 * type == -10 to -19 : monster casting spell
3853 * type == -20 to -29 : monster breathing at you
3854 * type == -30 to -39 : monster shooting a wand
3855 * called with dx = dy = 0 with vertical bolts
3857 void
3858 buzz(type, nd, sx, sy, dx, dy)
3859 register int type, nd;
3860 register xchar sx, sy;
3861 register int dx, dy;
3863 int range, abstype = abs(type) % 10;
3864 register xchar lsx, lsy;
3865 struct monst *mon;
3866 coord save_bhitpos;
3867 boolean shopdamage = FALSE;
3868 const char *fltxt;
3869 struct obj *otmp;
3870 int spell_type;
3872 /* if its a Hero Spell then get its SPE_TYPE */
3873 spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + abstype : 0;
3875 fltxt = flash_types[(type <= -30) ? abstype : abs(type)];
3876 if (u.uswallow) {
3877 register int tmp;
3879 if (type < 0)
3880 return;
3881 tmp = zhitm(u.ustuck, type, nd, &otmp);
3882 if (!u.ustuck)
3883 u.uswallow = 0;
3884 else
3885 pline("%s rips into %s%s", The(fltxt), mon_nam(u.ustuck),
3886 exclam(tmp));
3887 /* Using disintegration from the inside only makes a hole... */
3888 if (tmp == MAGIC_COOKIE)
3889 u.ustuck->mhp = 0;
3890 if (u.ustuck->mhp < 1)
3891 killed(u.ustuck);
3892 return;
3894 if (type < 0)
3895 newsym(u.ux, u.uy);
3896 range = rn1(7, 7);
3897 if (dx == 0 && dy == 0)
3898 range = 1;
3899 save_bhitpos = bhitpos;
3901 tmp_at(DISP_BEAM, zapdir_to_glyph(dx, dy, abstype));
3902 while (range-- > 0) {
3903 lsx = sx;
3904 sx += dx;
3905 lsy = sy;
3906 sy += dy;
3907 if (!isok(sx, sy) || levl[sx][sy].typ == STONE)
3908 goto make_bounce;
3910 mon = m_at(sx, sy);
3911 if (cansee(sx, sy)) {
3912 /* reveal/unreveal invisible monsters before tmp_at() */
3913 if (mon && !canspotmon(mon))
3914 map_invisible(sx, sy);
3915 else if (!mon && glyph_is_invisible(levl[sx][sy].glyph)) {
3916 unmap_object(sx, sy);
3917 newsym(sx, sy);
3919 if (ZAP_POS(levl[sx][sy].typ)
3920 || (isok(lsx, lsy) && cansee(lsx, lsy)))
3921 tmp_at(sx, sy);
3922 delay_output(); /* wait a little */
3925 /* hit() and miss() need bhitpos to match the target */
3926 bhitpos.x = sx, bhitpos.y = sy;
3927 /* Fireballs only damage when they explode */
3928 if (type != ZT_SPELL(ZT_FIRE))
3929 range += zap_over_floor(sx, sy, type, &shopdamage, 0);
3931 if (mon) {
3932 if (type == ZT_SPELL(ZT_FIRE))
3933 break;
3934 if (type >= 0)
3935 mon->mstrategy &= ~STRAT_WAITMASK;
3936 buzzmonst:
3937 notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y);
3938 if (zap_hit(find_mac(mon), spell_type)) {
3939 if (mon_reflects(mon, (char *) 0)) {
3940 if (cansee(mon->mx, mon->my)) {
3941 hit(fltxt, mon, exclam(0));
3942 shieldeff(mon->mx, mon->my);
3943 (void) mon_reflects(mon,
3944 "But it reflects from %s %s!");
3946 dx = -dx;
3947 dy = -dy;
3948 } else {
3949 boolean mon_could_move = mon->mcanmove;
3950 int tmp = zhitm(mon, type, nd, &otmp);
3952 if (is_rider(mon->data)
3953 && abs(type) == ZT_BREATH(ZT_DEATH)) {
3954 if (canseemon(mon)) {
3955 hit(fltxt, mon, ".");
3956 pline("%s disintegrates.", Monnam(mon));
3957 pline("%s body reintegrates before your %s!",
3958 s_suffix(Monnam(mon)),
3959 (eyecount(youmonst.data) == 1)
3960 ? body_part(EYE)
3961 : makeplural(body_part(EYE)));
3962 pline("%s resurrects!", Monnam(mon));
3964 mon->mhp = mon->mhpmax;
3965 break; /* Out of while loop */
3967 if (mon->data == &mons[PM_DEATH] && abstype == ZT_DEATH) {
3968 if (canseemon(mon)) {
3969 hit(fltxt, mon, ".");
3970 pline("%s absorbs the deadly %s!", Monnam(mon),
3971 type == ZT_BREATH(ZT_DEATH) ? "blast"
3972 : "ray");
3973 pline("It seems even stronger than before.");
3975 break; /* Out of while loop */
3978 if (tmp == MAGIC_COOKIE) { /* disintegration */
3979 disintegrate_mon(mon, type, fltxt);
3980 } else if (mon->mhp < 1) {
3981 if (type < 0)
3982 monkilled(mon, fltxt, AD_RBRE);
3983 else
3984 killed(mon);
3985 } else {
3986 if (!otmp) {
3987 /* normal non-fatal hit */
3988 hit(fltxt, mon, exclam(tmp));
3989 } else {
3990 /* some armor was destroyed; no damage done */
3991 if (canseemon(mon))
3992 pline("%s %s is disintegrated!",
3993 s_suffix(Monnam(mon)),
3994 distant_name(otmp, xname));
3995 m_useup(mon, otmp);
3997 if (mon_could_move && !mon->mcanmove) /* ZT_SLEEP */
3998 slept_monst(mon);
4001 range -= 2;
4002 } else {
4003 miss(fltxt, mon);
4005 } else if (sx == u.ux && sy == u.uy && range >= 0) {
4006 nomul(0);
4007 if (u.usteed && !rn2(3) && !mon_reflects(u.usteed, (char *) 0)) {
4008 mon = u.usteed;
4009 goto buzzmonst;
4010 } else if (zap_hit((int) u.uac, 0)) {
4011 range -= 2;
4012 pline("%s hits you!", The(fltxt));
4013 if (Reflecting) {
4014 if (!Blind) {
4015 (void) ureflects("But %s reflects from your %s!",
4016 "it");
4017 } else
4018 pline("For some reason you are not affected.");
4019 dx = -dx;
4020 dy = -dy;
4021 shieldeff(sx, sy);
4022 } else {
4023 zhitu(type, nd, fltxt, sx, sy);
4025 } else if (!Blind) {
4026 pline("%s whizzes by you!", The(fltxt));
4027 } else if (abstype == ZT_LIGHTNING) {
4028 Your("%s tingles.", body_part(ARM));
4030 if (abstype == ZT_LIGHTNING)
4031 (void) flashburn((long) d(nd, 50));
4032 stop_occupation();
4033 nomul(0);
4036 if (!ZAP_POS(levl[sx][sy].typ)
4037 || (closed_door(sx, sy) && range >= 0)) {
4038 int bounce;
4039 uchar rmn;
4040 boolean fireball;
4042 make_bounce:
4043 bounce = 0;
4044 fireball = (type == ZT_SPELL(ZT_FIRE));
4045 if ((--range > 0 && isok(lsx, lsy) && cansee(lsx, lsy))
4046 || fireball) {
4047 if (Is_airlevel(&u.uz)) { /* nothing to bounce off of */
4048 pline_The("%s vanishes into the aether!", fltxt);
4049 if (fireball)
4050 type = ZT_WAND(ZT_FIRE); /* skip pending fireball */
4051 break;
4052 } else if (fireball) {
4053 sx = lsx;
4054 sy = lsy;
4055 break; /* fireballs explode before the obstacle */
4056 } else
4057 pline_The("%s bounces!", fltxt);
4059 if (!dx || !dy || !rn2(20)) {
4060 dx = -dx;
4061 dy = -dy;
4062 } else {
4063 if (isok(sx, lsy) && ZAP_POS(rmn = levl[sx][lsy].typ)
4064 && !closed_door(sx, lsy)
4065 && (IS_ROOM(rmn) || (isok(sx + dx, lsy)
4066 && ZAP_POS(levl[sx + dx][lsy].typ))))
4067 bounce = 1;
4068 if (isok(lsx, sy) && ZAP_POS(rmn = levl[lsx][sy].typ)
4069 && !closed_door(lsx, sy)
4070 && (IS_ROOM(rmn) || (isok(lsx, sy + dy)
4071 && ZAP_POS(levl[lsx][sy + dy].typ))))
4072 if (!bounce || rn2(2))
4073 bounce = 2;
4075 switch (bounce) {
4076 case 0:
4077 dx = -dx; /* fall into... */
4078 case 1:
4079 dy = -dy;
4080 break;
4081 case 2:
4082 dx = -dx;
4083 break;
4085 tmp_at(DISP_CHANGE, zapdir_to_glyph(dx, dy, abstype));
4089 tmp_at(DISP_END, 0);
4090 if (type == ZT_SPELL(ZT_FIRE))
4091 explode(sx, sy, type, d(12, 6), 0, EXPL_FIERY);
4092 if (shopdamage)
4093 pay_for_damage(abstype == ZT_FIRE
4094 ? "burn away"
4095 : abstype == ZT_COLD
4096 ? "shatter"
4097 /* "damage" indicates wall rather than door */
4098 : abstype == ZT_ACID
4099 ? "damage"
4100 : abstype == ZT_DEATH
4101 ? "disintegrate"
4102 : "destroy",
4103 FALSE);
4104 bhitpos = save_bhitpos;
4107 void
4108 melt_ice(x, y, msg)
4109 xchar x, y;
4110 const char *msg;
4112 struct rm *lev = &levl[x][y];
4113 struct obj *otmp;
4115 if (!msg)
4116 msg = "The ice crackles and melts.";
4117 if (lev->typ == DRAWBRIDGE_UP) {
4118 lev->drawbridgemask &= ~DB_ICE; /* revert to DB_MOAT */
4119 } else { /* lev->typ == ICE */
4120 #ifdef STUPID
4121 if (lev->icedpool == ICED_POOL)
4122 lev->typ = POOL;
4123 else
4124 lev->typ = MOAT;
4125 #else
4126 lev->typ = (lev->icedpool == ICED_POOL ? POOL : MOAT);
4127 #endif
4128 lev->icedpool = 0;
4130 spot_stop_timers(x, y, MELT_ICE_AWAY); /* no more ice to melt away */
4131 obj_ice_effects(x, y, FALSE);
4132 unearth_objs(x, y);
4133 if (Underwater)
4134 vision_recalc(1);
4135 newsym(x, y);
4136 if (cansee(x, y))
4137 Norep("%s", msg);
4138 if ((otmp = sobj_at(BOULDER, x, y)) != 0) {
4139 if (cansee(x, y))
4140 pline("%s settles...", An(xname(otmp)));
4141 do {
4142 obj_extract_self(otmp); /* boulder isn't being pushed */
4143 if (!boulder_hits_pool(otmp, x, y, FALSE))
4144 impossible("melt_ice: no pool?");
4145 /* try again if there's another boulder and pool didn't fill */
4146 } while (is_pool(x, y) && (otmp = sobj_at(BOULDER, x, y)) != 0);
4147 newsym(x, y);
4149 if (x == u.ux && y == u.uy)
4150 spoteffects(TRUE); /* possibly drown, notice objects */
4153 #define MIN_ICE_TIME 50
4154 #define MAX_ICE_TIME 2000
4156 * Usually start a melt_ice timer; sometimes the ice will become
4157 * permanent instead.
4159 void
4160 start_melt_ice_timeout(x, y, min_time)
4161 xchar x, y;
4162 long min_time; /* <x,y>'s old melt timeout (deleted by time we get here) */
4164 int when;
4165 long where;
4167 when = (int) min_time;
4168 if (when < MIN_ICE_TIME - 1)
4169 when = MIN_ICE_TIME - 1;
4171 /* random timeout; surrounding ice locations ought to be a factor... */
4172 while (++when <= MAX_ICE_TIME)
4173 if (!rn2((MAX_ICE_TIME - when) + MIN_ICE_TIME))
4174 break;
4176 /* if we're within MAX_ICE_TIME, install a melt timer;
4177 otherwise, omit it to leave this ice permanent */
4178 if (when <= MAX_ICE_TIME) {
4179 where = ((long) x << 16) | (long) y;
4180 (void) start_timer((long) when, TIMER_LEVEL, MELT_ICE_AWAY,
4181 long_to_any(where));
4184 #undef MIN_ICE_TIME
4185 #undef MAX_ICE_TIME
4188 * Called when ice has melted completely away.
4190 void
4191 melt_ice_away(arg, timeout)
4192 anything *arg;
4193 long timeout UNUSED;
4195 xchar x, y;
4196 long where = arg->a_long;
4198 y = (xchar) (where & 0xFFFF);
4199 x = (xchar) ((where >> 16) & 0xFFFF);
4200 /* melt_ice does newsym when appropriate */
4201 melt_ice(x, y, "Some ice melts away.");
4204 /* Burn floor scrolls, evaporate pools, etc... in a single square.
4205 * Used both for normal bolts of fire, cold, etc... and for fireballs.
4206 * Sets shopdamage to TRUE if a shop door is destroyed, and returns the
4207 * amount by which range is reduced (the latter is just ignored by fireballs)
4210 zap_over_floor(x, y, type, shopdamage, exploding_wand_typ)
4211 xchar x, y;
4212 int type;
4213 boolean *shopdamage;
4214 short exploding_wand_typ;
4216 const char *zapverb;
4217 struct monst *mon;
4218 struct trap *t;
4219 struct rm *lev = &levl[x][y];
4220 boolean see_it = cansee(x, y), yourzap;
4221 int rangemod = 0, abstype = abs(type) % 10;
4223 switch (abstype) {
4224 case ZT_FIRE:
4225 t = t_at(x, y);
4226 if (t && t->ttyp == WEB) {
4227 /* a burning web is too flimsy to notice if you can't see it */
4228 if (see_it)
4229 Norep("A web bursts into flames!");
4230 (void) delfloortrap(t);
4231 if (see_it)
4232 newsym(x, y);
4234 if (is_ice(x, y)) {
4235 melt_ice(x, y, (char *) 0);
4236 } else if (is_pool(x, y)) {
4237 const char *msgtxt = "You hear hissing gas.";
4239 if (lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP */
4240 if (see_it)
4241 msgtxt = "Some water evaporates.";
4242 } else {
4243 rangemod -= 3;
4244 lev->typ = ROOM;
4245 t = maketrap(x, y, PIT);
4246 if (t)
4247 t->tseen = 1;
4248 if (see_it)
4249 msgtxt = "The water evaporates.";
4251 Norep("%s", msgtxt);
4252 if (lev->typ == ROOM)
4253 newsym(x, y);
4254 } else if (IS_FOUNTAIN(lev->typ)) {
4255 if (see_it)
4256 pline("Steam billows from the fountain.");
4257 rangemod -= 1;
4258 dryup(x, y, type > 0);
4260 break; /* ZT_FIRE */
4262 case ZT_COLD:
4263 if (is_pool(x, y) || is_lava(x, y)) {
4264 boolean lava = is_lava(x, y),
4265 moat = is_moat(x, y);
4267 if (lev->typ == WATER) {
4268 /* For now, don't let WATER freeze. */
4269 if (see_it)
4270 pline_The("water freezes for a moment.");
4271 else
4272 You_hear("a soft crackling.");
4273 rangemod -= 1000; /* stop */
4274 } else {
4275 char buf[BUFSZ];
4277 Strcpy(buf, waterbody_name(x, y)); /* for MOAT */
4278 rangemod -= 3;
4279 if (lev->typ == DRAWBRIDGE_UP) {
4280 lev->drawbridgemask &= ~DB_UNDER; /* clear lava */
4281 lev->drawbridgemask |= (lava ? DB_FLOOR : DB_ICE);
4282 } else {
4283 if (!lava)
4284 lev->icedpool = (lev->typ == POOL) ? ICED_POOL
4285 : ICED_MOAT;
4286 lev->typ = lava ? ROOM : ICE;
4288 bury_objs(x, y);
4289 if (see_it) {
4290 if (lava)
4291 Norep("The lava cools and solidifies.");
4292 else if (moat)
4293 Norep("The %s is bridged with ice!", buf);
4294 else
4295 Norep("The water freezes.");
4296 newsym(x, y);
4297 } else if (!lava)
4298 You_hear("a crackling sound.");
4300 if (x == u.ux && y == u.uy) {
4301 if (u.uinwater) { /* not just `if (Underwater)' */
4302 /* leave the no longer existent water */
4303 u.uinwater = 0;
4304 u.uundetected = 0;
4305 docrt();
4306 vision_full_recalc = 1;
4307 } else if (u.utrap && u.utraptype == TT_LAVA) {
4308 if (Passes_walls) {
4309 u.utrap = 0;
4310 You("pass through the now-solid rock.");
4311 } else {
4312 u.utrap = rn1(50, 20);
4313 u.utraptype = TT_INFLOOR;
4314 You("are firmly stuck in the cooling rock.");
4317 } else if ((mon = m_at(x, y)) != 0) {
4318 /* probably ought to do some hefty damage to any
4319 non-ice creature caught in freezing water;
4320 at a minimum, eels are forced out of hiding */
4321 if (is_swimmer(mon->data) && mon->mundetected) {
4322 mon->mundetected = 0;
4323 newsym(x, y);
4326 if (!lava) {
4327 start_melt_ice_timeout(x, y, 0L);
4328 obj_ice_effects(x, y, TRUE);
4330 } /* ?WATER */
4332 } else if (is_ice(x, y)) {
4333 long melt_time;
4335 /* Already ice here, so just firm it up. */
4336 /* Now ensure that only ice that is already timed is affected */
4337 if ((melt_time = spot_time_left(x, y, MELT_ICE_AWAY)) != 0L) {
4338 spot_stop_timers(x, y, MELT_ICE_AWAY);
4339 start_melt_ice_timeout(x, y, melt_time);
4342 break; /* ZT_COLD */
4344 case ZT_POISON_GAS:
4345 (void) create_gas_cloud(x, y, 1, 8);
4346 break;
4348 case ZT_ACID:
4349 if (lev->typ == IRONBARS) {
4350 if ((lev->wall_info & W_NONDIGGABLE) != 0) {
4351 if (see_it)
4352 Norep("The %s corrode somewhat but remain intact.",
4353 defsyms[S_bars].explanation);
4354 /* but nothing actually happens... */
4355 } else {
4356 rangemod -= 3;
4357 if (see_it)
4358 Norep("The %s melt.", defsyms[S_bars].explanation);
4359 if (*in_rooms(x, y, SHOPBASE)) {
4360 /* in case we ever have a shop bounded by bars */
4361 lev->typ = ROOM;
4362 if (see_it)
4363 newsym(x, y);
4364 add_damage(x, y, (type >= 0) ? 300L : 0L);
4365 if (type >= 0)
4366 *shopdamage = TRUE;
4367 } else {
4368 lev->typ = DOOR;
4369 lev->doormask = D_NODOOR;
4370 if (see_it)
4371 newsym(x, y);
4375 break; /* ZT_ACID */
4377 default:
4378 break;
4381 /* set up zap text for possible door feedback; for exploding wand, we
4382 want "the blast" rather than "your blast" even if hero caused it */
4383 yourzap = (type >= 0 && !exploding_wand_typ);
4384 zapverb = "blast"; /* breath attack or wand explosion */
4385 if (!exploding_wand_typ) {
4386 if (abs(type) < ZT_SPELL(0))
4387 zapverb = "bolt"; /* wand zap */
4388 else if (abs(type) < ZT_BREATH(0))
4389 zapverb = "spell";
4392 /* secret door gets revealed, converted into regular door */
4393 if (levl[x][y].typ == SDOOR) {
4394 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
4395 /* target spot will now pass closed_door() test below
4396 (except on rogue level) */
4397 newsym(x, y);
4398 if (see_it)
4399 pline("%s %s reveals a secret door.",
4400 yourzap ? "Your" : "The", zapverb);
4401 else if (Is_rogue_level(&u.uz))
4402 draft_message(FALSE); /* "You feel a draft." (open doorway) */
4405 /* regular door absorbs remaining zap range, possibly gets destroyed */
4406 if (closed_door(x, y)) {
4407 int new_doormask = -1;
4408 const char *see_txt = 0, *sense_txt = 0, *hear_txt = 0;
4410 rangemod = -1000;
4411 switch (abstype) {
4412 case ZT_FIRE:
4413 new_doormask = D_NODOOR;
4414 see_txt = "The door is consumed in flames!";
4415 sense_txt = "smell smoke.";
4416 break;
4417 case ZT_COLD:
4418 new_doormask = D_NODOOR;
4419 see_txt = "The door freezes and shatters!";
4420 sense_txt = "feel cold.";
4421 break;
4422 case ZT_DEATH:
4423 /* death spells/wands don't disintegrate */
4424 if (abs(type) != ZT_BREATH(ZT_DEATH))
4425 goto def_case;
4426 new_doormask = D_NODOOR;
4427 see_txt = "The door disintegrates!";
4428 hear_txt = "crashing wood.";
4429 break;
4430 case ZT_LIGHTNING:
4431 new_doormask = D_BROKEN;
4432 see_txt = "The door splinters!";
4433 hear_txt = "crackling.";
4434 break;
4435 default:
4436 def_case:
4437 if (exploding_wand_typ > 0) {
4438 /* Magical explosion from misc exploding wand */
4439 if (exploding_wand_typ == WAN_STRIKING) {
4440 new_doormask = D_BROKEN;
4441 see_txt = "The door crashes open!";
4442 sense_txt = "feel a burst of cool air.";
4443 break;
4446 if (see_it) {
4447 /* "the door absorbs the blast" would be
4448 inaccurate for an exploding wand since
4449 other adjacent locations still get hit */
4450 if (exploding_wand_typ)
4451 pline_The("door remains intact.");
4452 else
4453 pline_The("door absorbs %s %s!", yourzap ? "your" : "the",
4454 zapverb);
4455 } else
4456 You_feel("vibrations.");
4457 break;
4459 if (new_doormask >= 0) { /* door gets broken */
4460 if (*in_rooms(x, y, SHOPBASE)) {
4461 if (type >= 0) {
4462 add_damage(x, y, 400L);
4463 *shopdamage = TRUE;
4464 } else /* caused by monster */
4465 add_damage(x, y, 0L);
4467 lev->doormask = new_doormask;
4468 unblock_point(x, y); /* vision */
4469 if (see_it) {
4470 pline1(see_txt);
4471 newsym(x, y);
4472 } else if (sense_txt) {
4473 You1(sense_txt);
4474 } else if (hear_txt)
4475 You_hear1(hear_txt);
4476 if (picking_at(x, y)) {
4477 stop_occupation();
4478 reset_pick();
4483 if (OBJ_AT(x, y) && abstype == ZT_FIRE)
4484 if (burn_floor_objects(x, y, FALSE, type > 0) && couldsee(x, y)) {
4485 newsym(x, y);
4486 You("%s of smoke.", !Blind ? "see a puff" : "smell a whiff");
4488 if ((mon = m_at(x, y)) != 0) {
4489 /* Cannot use wakeup() which also angers the monster */
4490 mon->msleeping = 0;
4491 if (mon->m_ap_type)
4492 seemimic(mon);
4493 if (type >= 0) {
4494 setmangry(mon);
4495 if (mon->ispriest && *in_rooms(mon->mx, mon->my, TEMPLE))
4496 ghod_hitsu(mon);
4497 if (mon->isshk && !*u.ushops)
4498 hot_pursuit(mon);
4501 return rangemod;
4504 /* fractured by pick-axe or wand of striking */
4505 void
4506 fracture_rock(obj)
4507 register struct obj *obj; /* no texts here! */
4509 xchar x, y;
4510 boolean by_you = !context.mon_moving;
4512 if (by_you && get_obj_location(obj, &x, &y, 0) && costly_spot(x, y)) {
4513 struct monst *shkp = 0;
4514 char objroom = *in_rooms(x, y, SHOPBASE);
4516 if (billable(&shkp, obj, objroom, FALSE)) {
4517 /* shop message says "you owe <shk> <$> for it!" so we need
4518 to precede that with a message explaining what "it" is */
4519 You("fracture %s %s.", s_suffix(shkname(shkp)), xname(obj));
4520 breakobj(obj, x, y, TRUE, FALSE); /* charges for shop goods */
4523 if (by_you && obj->otyp == BOULDER)
4524 sokoban_guilt();
4526 obj->otyp = ROCK;
4527 obj->oclass = GEM_CLASS;
4528 obj->quan = (long) rn1(60, 7);
4529 obj->owt = weight(obj);
4530 obj->dknown = obj->bknown = obj->rknown = 0;
4531 obj->known = objects[obj->otyp].oc_uses_known ? 0 : 1;
4532 dealloc_oextra(obj);
4534 if (obj->where == OBJ_FLOOR) {
4535 obj_extract_self(obj); /* move rocks back on top */
4536 place_object(obj, obj->ox, obj->oy);
4537 if (!does_block(obj->ox, obj->oy, &levl[obj->ox][obj->oy]))
4538 unblock_point(obj->ox, obj->oy);
4539 if (cansee(obj->ox, obj->oy))
4540 newsym(obj->ox, obj->oy);
4544 /* handle statue hit by striking/force bolt/pick-axe */
4545 boolean
4546 break_statue(obj)
4547 register struct obj *obj;
4549 /* [obj is assumed to be on floor, so no get_obj_location() needed] */
4550 struct trap *trap = t_at(obj->ox, obj->oy);
4551 struct obj *item;
4552 boolean by_you = !context.mon_moving;
4554 if (trap && trap->ttyp == STATUE_TRAP
4555 && activate_statue_trap(trap, obj->ox, obj->oy, TRUE))
4556 return FALSE;
4557 /* drop any objects contained inside the statue */
4558 while ((item = obj->cobj) != 0) {
4559 obj_extract_self(item);
4560 place_object(item, obj->ox, obj->oy);
4562 if (by_you && Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC)) {
4563 You_feel("guilty about damaging such a historic statue.");
4564 adjalign(-1);
4566 obj->spe = 0;
4567 fracture_rock(obj);
4568 return TRUE;
4572 * destroy_strings[dindx][0:singular,1:plural,2:killer_reason]
4573 * [0] freezing potion
4574 * [1] boiling potion other than oil
4575 * [2] boiling potion of oil
4576 * [3] burning scroll
4577 * [4] burning spellbook
4578 * [5] shocked ring
4579 * [6] shocked wand
4580 * (books, rings, and wands don't stack so don't need plural form;
4581 * crumbling ring doesn't do damage so doesn't need killer reason)
4583 const char *const destroy_strings[][3] = {
4584 /* also used in trap.c */
4585 { "freezes and shatters", "freeze and shatter", "shattered potion" },
4586 { "boils and explodes", "boil and explode", "boiling potion" },
4587 { "ignites and explodes", "ignite and explode", "exploding potion" },
4588 { "catches fire and burns", "catch fire and burn", "burning scroll" },
4589 { "catches fire and burns", "", "burning book" },
4590 { "turns to dust and vanishes", "", "" },
4591 { "breaks apart and explodes", "", "exploding wand" },
4594 void
4595 destroy_item(osym, dmgtyp)
4596 register int osym, dmgtyp;
4598 register struct obj *obj, *obj2;
4599 int dmg, xresist, skip;
4600 long i, cnt, quan;
4601 int dindx;
4602 const char *mult;
4603 boolean physical_damage;
4605 for (obj = invent; obj; obj = obj2) {
4606 obj2 = obj->nobj;
4607 physical_damage = FALSE;
4608 if (obj->oclass != osym)
4609 continue; /* test only objs of type osym */
4610 if (obj->oartifact)
4611 continue; /* don't destroy artifacts */
4612 if (obj->in_use && obj->quan == 1L)
4613 continue; /* not available */
4614 xresist = skip = 0;
4615 /* lint suppression */
4616 dmg = dindx = 0;
4617 quan = 0L;
4619 switch (dmgtyp) {
4620 case AD_COLD:
4621 if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
4622 quan = obj->quan;
4623 dindx = 0;
4624 dmg = rnd(4);
4625 } else
4626 skip++;
4627 break;
4628 case AD_FIRE:
4629 xresist = (Fire_resistance && obj->oclass != POTION_CLASS
4630 && obj->otyp != GLOB_OF_GREEN_SLIME);
4632 if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
4633 skip++;
4634 if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
4635 skip++;
4636 if (!Blind)
4637 pline("%s glows a strange %s, but remains intact.",
4638 The(xname(obj)), hcolor("dark red"));
4640 quan = obj->quan;
4641 switch (osym) {
4642 case POTION_CLASS:
4643 dindx = (obj->otyp != POT_OIL) ? 1 : 2;
4644 dmg = rnd(6);
4645 break;
4646 case SCROLL_CLASS:
4647 dindx = 3;
4648 dmg = 1;
4649 break;
4650 case SPBOOK_CLASS:
4651 dindx = 4;
4652 dmg = 1;
4653 break;
4654 case FOOD_CLASS:
4655 if (obj->otyp == GLOB_OF_GREEN_SLIME) {
4656 dindx = 1; /* boil and explode */
4657 dmg = (obj->owt + 19) / 20;
4658 } else {
4659 skip++;
4661 break;
4662 default:
4663 skip++;
4664 break;
4666 break;
4667 case AD_ELEC:
4668 xresist = (Shock_resistance && obj->oclass != RING_CLASS);
4669 quan = obj->quan;
4670 switch (osym) {
4671 case RING_CLASS:
4672 if (obj->otyp == RIN_SHOCK_RESISTANCE) {
4673 skip++;
4674 break;
4676 dindx = 5;
4677 dmg = 0;
4678 break;
4679 case WAND_CLASS:
4680 if (obj->otyp == WAN_LIGHTNING) {
4681 skip++;
4682 break;
4684 #if 0
4685 if (obj == current_wand) { skip++; break; }
4686 #endif
4687 dindx = 6;
4688 dmg = rnd(10);
4689 break;
4690 default:
4691 skip++;
4692 break;
4694 break;
4695 default:
4696 skip++;
4697 break;
4699 if (!skip) {
4700 if (obj->in_use)
4701 --quan; /* one will be used up elsewhere */
4702 for (i = cnt = 0L; i < quan; i++)
4703 if (!rn2(3))
4704 cnt++;
4706 if (!cnt)
4707 continue;
4708 mult = (cnt == quan)
4709 ? (quan > 1) ? "All of your " : "Your"
4710 : (cnt == 1L) ? "One of your" : "Some of your";
4711 pline("%s %s %s!", mult, xname(obj),
4712 destroy_strings[dindx][(cnt > 1L)]);
4713 if (osym == POTION_CLASS && dmgtyp != AD_COLD) {
4714 if (!breathless(youmonst.data) || haseyes(youmonst.data))
4715 potionbreathe(obj);
4717 if (obj->owornmask) {
4718 if (obj->owornmask & W_RING) /* ring being worn */
4719 Ring_gone(obj);
4720 else
4721 setnotworn(obj);
4723 if (obj == current_wand)
4724 current_wand = 0; /* destroyed */
4725 for (i = 0; i < cnt; i++)
4726 useup(obj);
4727 if (dmg) {
4728 if (xresist)
4729 You("aren't hurt!");
4730 else {
4731 const char *how = destroy_strings[dindx][2];
4732 boolean one = (cnt == 1L);
4734 if (dmgtyp == AD_FIRE && osym == FOOD_CLASS)
4735 how = "exploding glob of slime";
4736 if (physical_damage)
4737 dmg = Maybe_Half_Phys(dmg);
4738 losehp(dmg, one ? how : (const char *) makeplural(how),
4739 one ? KILLED_BY_AN : KILLED_BY);
4740 exercise(A_STR, FALSE);
4745 return;
4749 destroy_mitem(mtmp, osym, dmgtyp)
4750 struct monst *mtmp;
4751 int osym, dmgtyp;
4753 struct obj *obj, *obj2;
4754 int skip, tmp = 0;
4755 long i, cnt, quan;
4756 int dindx;
4757 boolean vis;
4759 if (mtmp == &youmonst) { /* this simplifies artifact_hit() */
4760 destroy_item(osym, dmgtyp);
4761 return 0; /* arbitrary; value doesn't matter to artifact_hit() */
4764 vis = canseemon(mtmp);
4765 for (obj = mtmp->minvent; obj; obj = obj2) {
4766 obj2 = obj->nobj;
4767 if (obj->oclass != osym)
4768 continue; /* test only objs of type osym */
4769 skip = 0;
4770 quan = 0L;
4771 dindx = 0;
4773 switch (dmgtyp) {
4774 case AD_COLD:
4775 if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
4776 quan = obj->quan;
4777 dindx = 0;
4778 tmp++;
4779 } else
4780 skip++;
4781 break;
4782 case AD_FIRE:
4783 if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
4784 skip++;
4785 if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
4786 skip++;
4787 if (vis)
4788 pline("%s glows a strange %s, but remains intact.",
4789 The(distant_name(obj, xname)), hcolor("dark red"));
4791 quan = obj->quan;
4792 switch (osym) {
4793 case POTION_CLASS:
4794 dindx = (obj->otyp != POT_OIL) ? 1 : 2;
4795 tmp++;
4796 break;
4797 case SCROLL_CLASS:
4798 dindx = 3;
4799 tmp++;
4800 break;
4801 case SPBOOK_CLASS:
4802 dindx = 4;
4803 tmp++;
4804 break;
4805 case FOOD_CLASS:
4806 if (obj->otyp == GLOB_OF_GREEN_SLIME) {
4807 dindx = 1; /* boil and explode */
4808 tmp += (obj->owt + 19) / 20;
4809 } else {
4810 skip++;
4812 break;
4813 default:
4814 skip++;
4815 break;
4817 break;
4818 case AD_ELEC:
4819 quan = obj->quan;
4820 switch (osym) {
4821 case RING_CLASS:
4822 if (obj->otyp == RIN_SHOCK_RESISTANCE) {
4823 skip++;
4824 break;
4826 dindx = 5;
4827 break;
4828 case WAND_CLASS:
4829 if (obj->otyp == WAN_LIGHTNING) {
4830 skip++;
4831 break;
4833 dindx = 6;
4834 tmp++;
4835 break;
4836 default:
4837 skip++;
4838 break;
4840 break;
4841 default:
4842 skip++;
4843 break;
4845 if (!skip) {
4846 for (i = cnt = 0L; i < quan; i++)
4847 if (!rn2(3))
4848 cnt++;
4850 if (!cnt)
4851 continue;
4852 if (vis)
4853 pline("%s%s %s!",
4854 (cnt == obj->quan) ? "" : (cnt > 1L) ? "Some of "
4855 : "One of ",
4856 (cnt == obj->quan) ? Yname2(obj) : yname(obj),
4857 destroy_strings[dindx][(cnt > 1L)]);
4858 for (i = 0; i < cnt; i++)
4859 m_useup(mtmp, obj);
4862 return tmp;
4866 resist(mtmp, oclass, damage, tell)
4867 struct monst *mtmp;
4868 char oclass;
4869 int damage, tell;
4871 int resisted;
4872 int alev, dlev;
4874 /* attack level */
4875 switch (oclass) {
4876 case WAND_CLASS:
4877 alev = 12;
4878 break;
4879 case TOOL_CLASS:
4880 alev = 10;
4881 break; /* instrument */
4882 case WEAPON_CLASS:
4883 alev = 10;
4884 break; /* artifact */
4885 case SCROLL_CLASS:
4886 alev = 9;
4887 break;
4888 case POTION_CLASS:
4889 alev = 6;
4890 break;
4891 case RING_CLASS:
4892 alev = 5;
4893 break;
4894 default:
4895 alev = u.ulevel;
4896 break; /* spell */
4898 /* defense level */
4899 dlev = (int) mtmp->m_lev;
4900 if (dlev > 50)
4901 dlev = 50;
4902 else if (dlev < 1)
4903 dlev = is_mplayer(mtmp->data) ? u.ulevel : 1;
4905 resisted = rn2(100 + alev - dlev) < mtmp->data->mr;
4906 if (resisted) {
4907 if (tell) {
4908 shieldeff(mtmp->mx, mtmp->my);
4909 pline("%s resists!", Monnam(mtmp));
4911 damage = (damage + 1) / 2;
4914 if (damage) {
4915 mtmp->mhp -= damage;
4916 if (mtmp->mhp < 1) {
4917 if (m_using)
4918 monkilled(mtmp, "", AD_RBRE);
4919 else
4920 killed(mtmp);
4923 return resisted;
4926 #define MAXWISHTRY 5
4928 STATIC_OVL void
4929 wishcmdassist(triesleft)
4930 int triesleft;
4932 static NEARDATA const char *
4933 wishinfo[] = {
4934 "Wish details:",
4936 "Enter the name of an object, such as \"potion of monster detection\",",
4937 "\"scroll labeled README\", \"elven mithril-coat\", or \"Grimtooth\"",
4938 "(without the quotes).",
4940 "For object types which come in stacks, you may specify a plural name",
4941 "such as \"potions of healing\", or specify a count, such as \"1000 gold",
4942 "pieces\", although that aspect of your wish might not be granted.",
4944 "You may also specify various prefix values which might be used to",
4945 "modify the item, such as \"uncursed\" or \"rustproof\" or \"+1\".",
4946 "Most modifiers shown when viewing your inventory can be specified.",
4948 "You may specify 'nothing' to explicitly decline this wish.",
4951 preserve_wishless[] = "Doing so will preserve 'wishless' conduct.",
4952 retry_info[] =
4953 "If you specify an unrecognized object name %s%s time%s,",
4954 retry_too[] = "a randomly chosen item will be granted.",
4955 suppress_cmdassist[] =
4956 "(Suppress this assistance with !cmdassist in your config file.)",
4957 *cardinals[] = { "zero", "one", "two", "three", "four", "five" },
4958 too_many[] = "too many";
4959 int i;
4960 winid win;
4961 char buf[BUFSZ];
4963 win = create_nhwindow(NHW_TEXT);
4964 if (!win)
4965 return;
4966 for (i = 0; i < SIZE(wishinfo) - 1; ++i)
4967 putstr(win, 0, wishinfo[i]);
4968 if (!u.uconduct.wishes)
4969 putstr(win, 0, preserve_wishless);
4970 putstr(win, 0, "");
4971 Sprintf(buf, retry_info,
4972 (triesleft >= 0 && triesleft < SIZE(cardinals))
4973 ? cardinals[triesleft]
4974 : too_many,
4975 (triesleft < MAXWISHTRY) ? " more" : "",
4976 plur(triesleft));
4977 putstr(win, 0, buf);
4978 putstr(win, 0, retry_too);
4979 putstr(win, 0, "");
4980 if (iflags.cmdassist)
4981 putstr(win, 0, suppress_cmdassist);
4982 display_nhwindow(win, FALSE);
4983 destroy_nhwindow(win);
4986 void
4987 makewish()
4989 char buf[BUFSZ], promptbuf[BUFSZ];
4990 struct obj *otmp, nothing;
4991 int tries = 0;
4993 promptbuf[0] = '\0';
4994 nothing = zeroobj; /* lint suppression; only its address matters */
4995 if (flags.verbose)
4996 You("may wish for an object.");
4997 retry:
4998 Strcpy(promptbuf, "For what do you wish");
4999 if (iflags.cmdassist && tries > 0)
5000 Strcat(promptbuf, " (enter 'help' for assistance)");
5001 Strcat(promptbuf, "?");
5002 getlin(promptbuf, buf);
5003 (void) mungspaces(buf);
5004 if (buf[0] == '\033') {
5005 buf[0] = '\0';
5006 } else if (!strcmpi(buf, "help")) {
5007 wishcmdassist(MAXWISHTRY - tries);
5008 goto retry;
5011 * Note: if they wished for and got a non-object successfully,
5012 * otmp == &zeroobj. That includes gold, or an artifact that
5013 * has been denied. Wishing for "nothing" requires a separate
5014 * value to remain distinct.
5016 otmp = readobjnam(buf, &nothing);
5017 if (!otmp) {
5018 pline("Nothing fitting that description exists in the game.");
5019 if (++tries < MAXWISHTRY)
5020 goto retry;
5021 pline1(thats_enough_tries);
5022 otmp = readobjnam((char *) 0, (struct obj *) 0);
5023 if (!otmp)
5024 return; /* for safety; should never happen */
5025 } else if (otmp == &nothing) {
5026 /* explicitly wished for "nothing", presumably attempting
5027 to retain wishless conduct */
5028 return;
5031 /* KMH, conduct */
5032 u.uconduct.wishes++;
5034 if (otmp != &zeroobj) {
5035 const char
5036 *verb = ((Is_airlevel(&u.uz) || u.uinwater) ? "slip" : "drop"),
5037 *oops_msg = (u.uswallow
5038 ? "Oops! %s out of your reach!"
5039 : (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
5040 || levl[u.ux][u.uy].typ < IRONBARS
5041 || levl[u.ux][u.uy].typ >= ICE)
5042 ? "Oops! %s away from you!"
5043 : "Oops! %s to the floor!");
5045 /* The(aobjnam()) is safe since otmp is unidentified -dlc */
5046 (void) hold_another_object(otmp, oops_msg,
5047 The(aobjnam(otmp, verb)),
5048 (const char *) 0);
5049 u.ublesscnt += rn1(100, 50); /* the gods take notice */
5053 /*zap.c*/