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