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