make rank() static again
[NetHack.git] / src / zap.c
blobae4a168896d1cf841163a6bbbcf824150d7de990
1 /* NetHack 3.7 zap.c $NHDT-Date: 1737344505 2025/01/19 19:41:45 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.562 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2013. */
4 /* NetHack may be freely redistributed. See license for details. */
6 #include "hack.h"
8 /* Disintegration rays have special treatment; corpses are never left.
9 * But the routine which calculates the damage is separate from the routine
10 * which kills the monster. The damage routine returns this cookie to
11 * indicate that the monster should be disintegrated.
13 #define MAGIC_COOKIE 1000
15 staticfn int zaptype(int);
16 staticfn void probe_objchain(struct obj *) NO_NNARGS;
17 staticfn boolean zombie_can_dig(coordxy x, coordxy y);
18 staticfn void polyuse(struct obj *, int, int) NO_NNARGS;
19 staticfn void create_polymon(struct obj *, int) NO_NNARGS;
20 staticfn int stone_to_flesh_obj(struct obj *) NONNULLARG1;
21 staticfn boolean zap_updown(struct obj *) NONNULLARG1;
22 staticfn void zhitu(int, int, const char *, coordxy, coordxy) NO_NNARGS;
23 staticfn void revive_egg(struct obj *) NONNULLARG1;
24 staticfn boolean zap_steed(struct obj *) NONNULLARG1;
25 staticfn void skiprange(int, int *, int *) NONNULLPTRS;
26 staticfn void maybe_explode_trap(struct trap *, struct obj *,
27 boolean *) NONNULLARG3;
28 staticfn void zap_map(coordxy, coordxy, struct obj *) NONNULLARG3;
29 staticfn int zap_hit(int, int);
30 staticfn void disintegrate_mon(struct monst *, int, const char *) NONNULLARG1;
31 staticfn int adtyp_to_prop(int);
32 staticfn void backfire(struct obj *) NONNULLARG1;
33 staticfn int zap_ok(struct obj *) NO_NNARGS;
34 /* all callers of boxlock_invent() pass a NONNULL obj, and boxlock
35 * boxlock_invent() calls boxlock() which has nonnull arg. */
36 staticfn void boxlock_invent(struct obj *) NONNULLARG1;
37 staticfn int spell_hit_bonus(int);
38 staticfn int maybe_destroy_item(struct monst *, struct obj *, int) NONNULLPTRS;
39 staticfn boolean destroyable(struct obj *, int);
41 staticfn void wishcmdassist(int);
43 #define ZT_MAGIC_MISSILE (AD_MAGM - 1)
44 #define ZT_FIRE (AD_FIRE - 1)
45 #define ZT_COLD (AD_COLD - 1)
46 #define ZT_SLEEP (AD_SLEE - 1)
47 #define ZT_DEATH (AD_DISN - 1) /* or disintegration */
48 #define ZT_LIGHTNING (AD_ELEC - 1)
49 #define ZT_POISON_GAS (AD_DRST - 1)
50 #define ZT_ACID (AD_ACID - 1)
51 /* 8 and 9 are currently unassigned */
53 #define ZT_WAND(x) (x)
54 #define ZT_SPELL(x) (10 + (x))
55 #define ZT_BREATH(x) (20 + (x))
57 #define is_hero_spell(type) ((type) >= 10 && (type) < 20)
59 #define M_IN_WATER(ptr) ((ptr)->mlet == S_EEL || cant_drown(ptr))
61 static const char are_blinded_by_the_flash[] = "are blinded by the flash!";
64 * A positive index means zapped/cast/breathed by hero.
65 * A negative index means zapped/cast/breathed by a monster, with value
66 * index fixup beyond abs() needed for wand zaps. Wand zaps for monster
67 * use -39..-30 rather than -9..-0 because -0 is ambiguous (same as 0).
69 static const char *const flash_types[] = {
70 "magic missile", /* Wands must be 0-9 */
71 "bolt of fire", "bolt of cold", "sleep ray", "death ray",
72 "bolt of lightning", "", "", "", "",
74 "magic missile", /* Spell equivalents must be 10-19 */
75 "fireball", "cone of cold", "sleep ray", "finger of death",
76 "bolt of lightning", /* there is no spell, used for retribution */
77 "", "", "", "",
79 "blast of missiles", /* Dragon breath equivalents 20-29*/
80 "blast of fire", "blast of frost", "blast of sleep gas",
81 "blast of disintegration", "blast of lightning",
82 "blast of poison gas", "blast of acid", "", ""
85 /* convert monster zap/spell/breath value to hero zap/spell/breath value */
86 staticfn int
87 zaptype(int type)
89 if (type <= -30 && -39 <= type) /* monster wand zap */
90 type += 30; /* first convert -39..-30 to -9..0 so that abs()
91 * will yield 0..9 (hero wand zap) for it */
92 type = abs(type);
93 return type;
97 * Recognizing unseen wands by zapping: in 3.4.3 and earlier, zapping
98 * most wand types while blind would add that type to the discoveries
99 * list even if it had never been seen (ie, picked up while blinded
100 * and shown in inventory as simply "a wand"). This behavior has been
101 * changed; now such wands won't be discovered. But if the type is
102 * already discovered, then the individual wand whose effect was just
103 * observed will be flagged as if seen. [You already know wands of
104 * striking; you zap "a wand" and observe striking effect (presumably
105 * by sound or touch); it'll become shown in inventory as "a wand of
106 * striking".]
108 * Unfortunately, the new behavior isn't really correct either. There
109 * should be an `eknown' bit for "effect known" added for wands (and
110 * for potions since quaffing one of a stack is similar) so that the
111 * particular wand which has been zapped would have its type become
112 * known (it would change from "a wand" to "a wand of striking", for
113 * example) without the type becoming discovered or other unknown wands
114 * of that type showing additional information. When blindness ends,
115 * all objects in inventory with the eknown bit set would be discovered
116 * and other items of the same type would become known as such.
119 /* wand discovery gets special handling when hero is blinded */
120 void
121 learnwand(struct obj *obj)
123 /* For a wand (or wand-like tool) zapped by the player, if the
124 effect was observable (determined by caller; usually seen, but
125 possibly heard or felt if the hero is blinded) then discover the
126 object type provided that the object itself is known (as more
127 than just "a wand"). If object type is already discovered and
128 we observed the effect, mark the individual wand as having been
129 seen. Suppress spells (which use fake spellbook object for `obj')
130 so that casting a spell won't re-discover its forgotten book. */
131 if (obj->oclass != SPBOOK_CLASS) {
132 /* if type already discovered, treat this item has having been seen
133 even if hero is currently blinded (skips redundant makeknown) */
134 if (objects[obj->otyp].oc_name_known) {
135 obj->dknown = 1; /* will usually be set already */
137 /* otherwise discover it if item itself has been or can be seen */
138 } else {
139 /* in case it was picked up while blind and then zapped without
140 examining inventory after regaining sight (bypassing xname) */
141 if (!Blind)
142 obj->dknown = 1;
143 /* make the discovery iff we know what we're manipulating */
144 if (obj->dknown)
145 makeknown(obj->otyp);
147 update_inventory();
152 * Routines for IMMEDIATE wands and spells.
153 * Also RAY or NODIR for wands that are being broken rather than zapped.
156 /* bhitm: monster mtmp was hit by the effect of wand or spell otmp */
158 bhitm(struct monst *mtmp, struct obj *otmp)
160 int ret = 0;
161 boolean wake = TRUE; /* Most 'zaps' should wake monster */
162 boolean reveal_invis = FALSE, learn_it = FALSE;
163 boolean dbldam = Role_if(PM_KNIGHT) && u.uhave.questart;
164 boolean skilled_spell, helpful_gesture = FALSE;
165 int dmg, otyp = otmp->otyp; /* otmp is not NULL */
166 const char *zap_type_text = "spell";
167 struct obj *obj;
168 boolean disguised_mimic = (mtmp->data->mlet == S_MIMIC
169 && M_AP_TYPE(mtmp) != M_AP_NOTHING);
170 /* box_or_door(): mimic appearances that have locks */
171 #define box_or_door(monst) \
172 ((M_AP_TYPE(monst) == M_AP_OBJECT \
173 && ((monst)->mappearance == CHEST \
174 || (monst)->mappearance == LARGE_BOX)) \
175 || (M_AP_TYPE(monst) == M_AP_FURNITURE \
176 /* is_cmap_door() tests S_symbol values, and */ \
177 /* mon->mappearance for furniture contains one of those */ \
178 && is_cmap_door((monst)->mappearance)))
180 if (engulfing_u(mtmp))
181 reveal_invis = FALSE;
183 gn.notonhead = (mtmp->mx != gb.bhitpos.x || mtmp->my != gb.bhitpos.y);
184 skilled_spell = (otmp->oclass == SPBOOK_CLASS && otmp->blessed);
186 switch (otyp) {
187 case WAN_STRIKING:
188 zap_type_text = "wand";
189 FALLTHROUGH;
190 /*FALLTHRU*/
191 case SPE_FORCE_BOLT:
192 reveal_invis = TRUE;
193 if (disguised_mimic)
194 seemimic(mtmp);
195 learn_it = cansee(gb.bhitpos.x, gb.bhitpos.y);
196 if (resists_magm(mtmp)) { /* match effect on player */
197 shieldeff(mtmp->mx, mtmp->my);
198 pline("Boing!");
199 /* 3.7: used to 'break' to avoid setting learn_it here */
200 } else if (u.uswallow || rnd(20) < 10 + find_mac(mtmp)) {
201 dmg = d(2, 12);
202 if (dbldam)
203 dmg *= 2;
204 if (otyp == SPE_FORCE_BOLT)
205 dmg = spell_damage_bonus(dmg);
206 hit(zap_type_text, mtmp, exclam(dmg));
207 (void) resist(mtmp, otmp->oclass, dmg, TELL);
208 } else {
209 miss(zap_type_text, mtmp);
210 learn_it = FALSE;
212 break;
213 case WAN_SLOW_MONSTER:
214 case SPE_SLOW_MONSTER:
215 if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
216 if (disguised_mimic)
217 seemimic(mtmp);
218 mon_adjust_speed(mtmp, -1, otmp);
219 check_gear_next_turn(mtmp); /* might want speed boots */
221 if (engulfing_u(mtmp) && is_whirly(mtmp->data)) {
222 You("disrupt %s!", mon_nam(mtmp));
223 pline("A huge hole opens up...");
224 expels(mtmp, mtmp->data, TRUE);
227 break;
228 case WAN_SPEED_MONSTER:
229 if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
230 if (disguised_mimic)
231 seemimic(mtmp);
232 mon_adjust_speed(mtmp, 1, otmp);
233 check_gear_next_turn(mtmp); /* might want speed boots */
235 /* wake but don't anger a peaceful target */
236 helpful_gesture = TRUE;
237 break;
238 case WAN_UNDEAD_TURNING:
239 case SPE_TURN_UNDEAD:
240 wake = FALSE;
241 if (unturn_dead(mtmp))
242 wake = TRUE;
243 if (is_undead(mtmp->data) || is_vampshifter(mtmp)) {
244 reveal_invis = TRUE;
245 wake = TRUE;
246 dmg = rnd(8);
247 if (dbldam)
248 dmg *= 2;
249 if (otyp == SPE_TURN_UNDEAD)
250 dmg = spell_damage_bonus(dmg);
251 svc.context.bypasses = TRUE; /* for make_corpse() */
252 if (!resist(mtmp, otmp->oclass, dmg, NOTELL)) {
253 if (!DEADMONSTER(mtmp))
254 monflee(mtmp, 0, FALSE, TRUE);
257 break;
258 case WAN_POLYMORPH:
259 case SPE_POLYMORPH:
260 case POT_POLYMORPH:
261 if (mtmp->data == &mons[PM_LONG_WORM] && has_mcorpsenm(mtmp)) {
262 /* if a long worm has mcorpsenm set, it was polymorphed by
263 the current zap and shouldn't be affected if hit again */
265 } else if (resists_magm(mtmp)) {
266 /* magic resistance protects from polymorph traps, so make
267 it guard against involuntary polymorph attacks too... */
268 shieldeff_mon(mtmp);
269 } else if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
270 boolean polyspot = (otyp != POT_POLYMORPH),
271 give_msg = (!Hallucination
272 && (canseemon(mtmp)
273 || engulfing_u(mtmp)));
275 /* dropped inventory (due to death by system shock,
276 or loss of wielded weapon and/or worn armor due to
277 limitations of new shape) won't be hit by this zap */
278 if (polyspot)
279 for (obj = mtmp->minvent; obj; obj = obj->nobj)
280 bypass_obj(obj);
282 /* natural shapechangers aren't affected by system shock
283 (unless protection from shapechangers is interfering
284 with their metabolism...) */
285 if (mtmp->cham == NON_PM && !rn2(25)) {
286 if (canseemon(mtmp)) {
287 pline("%s shudders!", Monnam(mtmp));
288 learn_it = TRUE;
290 /* svc.context.bypasses = TRUE; ## for make_corpse() */
291 /* no corpse after system shock */
292 xkilled(mtmp, XKILL_GIVEMSG | XKILL_NOCORPSE);
293 } else {
294 unsigned ncflags = NO_NC_FLAGS;
296 if (polyspot)
297 ncflags |= NC_VIA_WAND_OR_SPELL;
298 if (give_msg)
299 ncflags |= NC_SHOW_MSG;
300 if (newcham(mtmp, (struct permonst *) 0, ncflags) != 0
301 /* if shapechange failed because there aren't
302 enough eligible candidates (most likely for
303 vampshifter), try reverting to original form */
304 || (ismnum(mtmp->cham)
305 && newcham(mtmp, &mons[mtmp->cham],
306 ncflags) != 0)) {
307 if (give_msg && (canspotmon(mtmp)
308 || engulfing_u(mtmp)))
309 learn_it = TRUE;
313 /* do this even if polymorphed failed (otherwise using
314 flags.mon_polycontrol prompting to force mtmp to remain
315 'long worm' would prompt again if zap hit another segment) */
316 if (!DEADMONSTER(mtmp) && mtmp->data == &mons[PM_LONG_WORM]) {
317 if (!has_mcorpsenm(mtmp))
318 newmcorpsenm(mtmp);
319 /* flag to indicate that mtmp became a long worm
320 on current zap, so further hits (on mtmp's new
321 tail) don't do further transforms */
322 MCORPSENM(mtmp) = PM_LONG_WORM;
323 /* flag to indicate that cleanup is needed; object
324 bypass cleanup also clears mon->mextra->mcorpsenm
325 for all long worms on the level */
326 svc.context.bypasses = TRUE;
329 break;
330 case WAN_CANCELLATION:
331 case SPE_CANCELLATION:
332 if (disguised_mimic)
333 seemimic(mtmp);
334 (void) cancel_monst(mtmp, otmp, TRUE, TRUE, FALSE);
335 break;
336 case WAN_TELEPORTATION:
337 case SPE_TELEPORT_AWAY:
338 if (disguised_mimic)
339 seemimic(mtmp);
340 reveal_invis = !u_teleport_mon(mtmp, TRUE);
341 learn_it = canspotmon(mtmp);
342 break;
343 case WAN_MAKE_INVISIBLE: {
344 int oldinvis = mtmp->minvis;
345 boolean couldsee = canseemon(mtmp);
346 char nambuf[BUFSZ];
348 if (disguised_mimic)
349 seemimic(mtmp);
350 /* format monster's name before altering its visibility */
351 Strcpy(nambuf, Monnam(mtmp));
352 mon_set_minvis(mtmp);
353 if (!oldinvis && knowninvisible(mtmp)) {
354 pline("%s turns transparent!", nambuf);
355 reveal_invis = TRUE;
356 learn_it = TRUE;
357 } else if (couldsee && !canseemon(mtmp)) {
358 /* keep the immediate effects of make invisible and teleportation
359 ambiguous by using the same message that's used if we
360 teleported mtmp (and it ended up somewhere you can't see) */
361 pline("%s vanishes!", nambuf);
363 break;
365 case WAN_LOCKING:
366 case SPE_WIZARD_LOCK:
367 if (disguised_mimic && box_or_door(mtmp))
368 that_is_a_mimic(mtmp, TRUE); /*seemimic()*/
369 wake = closeholdingtrap(mtmp, &learn_it);
370 break;
371 case WAN_PROBING:
372 wake = FALSE;
373 reveal_invis = TRUE;
374 probe_monster(mtmp);
375 learn_it = TRUE;
376 break;
377 case WAN_OPENING:
378 case SPE_KNOCK:
379 if (disguised_mimic && box_or_door(mtmp))
380 that_is_a_mimic(mtmp, TRUE); /*seemimic()*/
381 wake = FALSE; /* don't want immediate counterattack */
382 if (mtmp == u.ustuck) {
383 /* zapping either holder/holdee or self [zapyourself()] will
384 release hero from holder's grasp or holdee from hero's grasp */
385 release_hold();
386 learn_it = TRUE;
388 /* zap which hits steed will only release saddle if it
389 doesn't hit a holding or falling trap; playability
390 here overrides the more logical target ordering */
391 } else if (openholdingtrap(mtmp, &learn_it)) {
392 break;
393 } else if (openfallingtrap(mtmp, TRUE, &learn_it)) {
394 /* mtmp might now be on the migrating monsters list */
395 break;
396 } else if (otyp == SPE_KNOCK) {
397 wake = TRUE;
398 ret = 1;
399 if (mtmp->data->msize < MZ_HUMAN && !m_is_steadfast(mtmp)) {
400 if (canseemon(mtmp))
401 pline("%s is knocked back!",
402 Monnam(mtmp));
403 mhurtle(mtmp, mtmp->mx - u.ux, mtmp->my - u.uy, rnd(2));
404 } else {
405 if (canseemon(mtmp))
406 pline("%s doesn't budge.", Monnam(mtmp));
408 if (!DEADMONSTER(mtmp)) {
409 wakeup(mtmp, !mindless(mtmp->data));
410 abuse_dog(mtmp);
412 } else if ((obj = which_armor(mtmp, W_SADDLE)) != 0) {
413 char buf[BUFSZ];
415 Sprintf(buf, "%s %s", s_suffix(Monnam(mtmp)),
416 distant_name(obj, xname));
417 if (cansee(mtmp->mx, mtmp->my)) {
418 if (!canspotmon(mtmp))
419 Strcpy(buf, An(distant_name(obj, xname)));
420 pline("%s falls to the %s.", buf,
421 surface(mtmp->mx, mtmp->my));
422 } else if (canspotmon(mtmp)) {
423 pline("%s falls off.", buf);
425 mdrop_obj(mtmp, obj, FALSE);
427 break;
428 case SPE_HEALING:
429 case SPE_EXTRA_HEALING: {
430 int healamt = d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4);
432 reveal_invis = TRUE;
433 if (mtmp->data != &mons[PM_PESTILENCE]) {
434 int delta = mtmp->mhpmax - mtmp->mhp;
436 wake = FALSE; /* wakeup() makes the target angry */
437 healmon(mtmp, healamt, 0);
438 /* plain healing must be blessed to cure blindness; extra
439 healing only needs to not be cursed, so spell always cures
440 [potions quaffed by monsters behave slightly differently;
441 we use the rules for the hero here...] */
442 if (skilled_spell || otyp == SPE_EXTRA_HEALING)
443 mcureblindness(mtmp, canseemon(mtmp));
444 if (canseemon(mtmp)) {
445 if (disguised_mimic) {
446 if (is_obj_mappear(mtmp, STRANGE_OBJECT)) {
447 /* it can do better now */
448 set_mimic_sym(mtmp);
449 newsym(mtmp->mx, mtmp->my);
450 } else
451 mimic_hit_msg(mtmp, otyp);
452 } else
453 pline("%s looks%s better.", Monnam(mtmp),
454 otyp == SPE_EXTRA_HEALING ? " much" : "");
456 if (mtmp->mtame && Role_if(PM_HEALER) && (delta > 0)) {
457 more_experienced(min(delta, healamt), 0);
458 newexplevel();
460 if (mtmp->mtame || mtmp->mpeaceful) {
461 adjalign(Role_if(PM_HEALER) ? 1 : sgn(u.ualign.type));
463 } else { /* Pestilence */
464 /* Pestilence will always resist; damage is half of (healamt/2) */
465 (void) resist(mtmp, otmp->oclass, healamt / 2, TELL);
467 break;
469 case WAN_LIGHT: /* (broken wand) */
470 if (flash_hits_mon(mtmp, otmp)) {
471 learn_it = TRUE;
472 reveal_invis = TRUE;
474 break;
475 case WAN_SLEEP: /* (broken wand) */
476 /* [wakeup() doesn't rouse victims of temporary sleep,
477 so it's okay to leave `wake' set to TRUE here;
478 revealing concealed mimic is handled by sleep_monst()] */
479 reveal_invis = TRUE;
480 if (sleep_monst(mtmp, d(1 + otmp->spe, 12), WAND_CLASS))
481 slept_monst(mtmp);
482 if (!Blind)
483 learn_it = TRUE;
484 break;
485 case SPE_STONE_TO_FLESH:
486 /* FIXME: mimics disguished as stone furniture or stone object
487 should be taken out of concealment. */
488 if (monsndx(mtmp->data) == PM_STONE_GOLEM) {
489 char *name = Monnam(mtmp);
491 /* turn into flesh golem */
492 if (newcham(mtmp, &mons[PM_FLESH_GOLEM], NO_NC_FLAGS)) {
493 if (canseemon(mtmp))
494 pline("%s turns to flesh!", name);
495 } else {
496 if (canseemon(mtmp))
497 pline("%s looks rather fleshy for a moment.", name);
499 } else
500 wake = FALSE;
501 break;
502 case SPE_DRAIN_LIFE:
503 if (disguised_mimic)
504 seemimic(mtmp);
505 dmg = monhp_per_lvl(mtmp);
506 if (dbldam)
507 dmg *= 2;
508 if (otyp == SPE_DRAIN_LIFE)
509 dmg = spell_damage_bonus(dmg);
510 if (resists_drli(mtmp)) {
511 shieldeff_mon(mtmp);
512 } else if (!resist(mtmp, otmp->oclass, dmg, NOTELL)
513 && !DEADMONSTER(mtmp)) {
514 mtmp->mhp -= dmg;
515 mtmp->mhpmax -= dmg;
516 /* die if already level 0, regardless of hit points */
517 if (DEADMONSTER(mtmp) || mtmp->mhpmax <= 0 || mtmp->m_lev < 1) {
518 killed(mtmp);
519 } else {
520 mtmp->m_lev--;
521 if (canseemon(mtmp))
522 pline("%s suddenly seems weaker!", Monnam(mtmp));
525 break;
526 case WAN_NOTHING:
527 wake = FALSE;
528 break;
529 default:
530 impossible("What an interesting effect (%d)", otyp);
531 break;
533 if (wake) {
534 if (!DEADMONSTER(mtmp)) {
535 wakeup(mtmp, helpful_gesture ? FALSE : TRUE);
536 m_respond(mtmp);
537 if (mtmp->isshk && !*u.ushops)
538 hot_pursuit(mtmp);
539 } else if (M_AP_TYPE(mtmp))
540 seemimic(mtmp); /* might unblock if mimicking a boulder/door */
542 /* note: gb.bhitpos won't be set if swallowed, but that's okay since
543 * reveal_invis will be false. We can't use mtmp->mx, my since it
544 * might be an invisible worm hit on the tail.
546 if (reveal_invis) {
547 if (!DEADMONSTER(mtmp) && cansee(gb.bhitpos.x, gb.bhitpos.y)
548 && !canspotmon(mtmp))
549 map_invisible(gb.bhitpos.x, gb.bhitpos.y);
551 /* if effect was observable then discover the wand type provided
552 that the wand itself has been seen */
553 if (learn_it)
554 learnwand(otmp);
555 return ret;
556 #undef box_or_door
559 /* hero is held by a monster or engulfed or holding a monster and has zapped
560 opening/unlocking magic at holder/engulfer/holdee or at self */
561 void
562 release_hold(void)
564 struct monst *mtmp = u.ustuck;
566 if (!mtmp) {
567 impossible("release_hold when not held?");
568 } else if (u.uswallow) { /* possible for sticky hero to be swallowed */
569 if (digests(mtmp->data)) {
570 if (!Blind)
571 pline("%s opens its mouth!", Monnam(mtmp));
572 else
573 You_feel("a sudden rush of air!");
575 /* gives "you get regurgitated" or "you get expelled from <mon>" */
576 expels(mtmp, mtmp->data, TRUE);
577 } else if (sticks(gy.youmonst.data)) {
578 /* order matters if 'holding' status condition is enabled;
579 set_ustuck() will set flag for botl update, You() pline will
580 trigger a status update with "UHold" removed */
581 set_ustuck((struct monst *) 0);
582 You("release %s.", mon_nam(mtmp));
583 } else { /* held but not swallowed */
584 char relbuf[BUFSZ];
586 unstuck(u.ustuck);
587 if (!nohands(mtmp->data))
588 Sprintf(relbuf, "from %s grasp", s_suffix(mon_nam(mtmp)));
589 else
590 Sprintf(relbuf, "by %s", mon_nam(mtmp));
591 You("are released %s.", relbuf);
595 staticfn void
596 probe_objchain(struct obj *otmp)
598 for (; otmp; otmp = otmp->nobj) {
599 otmp->dknown = 1; /* treat as "seen" */
600 if (Is_container(otmp) || otmp->otyp == STATUE) {
601 otmp->lknown = 1;
602 if (!SchroedingersBox(otmp))
603 otmp->cknown = 1;
604 } else if (otmp->otyp == TIN)
605 otmp->known = 1;
609 void
610 probe_monster(struct monst *mtmp)
612 mstatusline(mtmp);
613 if (gn.notonhead)
614 return; /* don't show minvent for long worm tail */
616 if (mtmp->minvent) {
617 probe_objchain(mtmp->minvent);
618 (void) display_minventory(mtmp, MINV_ALL | MINV_NOLET | PICK_NONE,
619 (char *) 0);
620 } else {
621 pline("%s is not carrying anything%s.", noit_Monnam(mtmp),
622 engulfing_u(mtmp) ? " besides you" : "");
627 * Return the object's physical location. This only makes sense for
628 * objects that are currently on the level (i.e. migrating objects
629 * are nowhere). By default, only things that can be seen (in hero's
630 * inventory, monster's inventory, or on the ground) are reported.
631 * By adding BURIED_TOO and/or CONTAINED_TOO flags, you can also get
632 * the location of buried and contained objects. Note that if an
633 * object is carried by a monster, its reported position may change
634 * from turn to turn. This function returns FALSE if the position
635 * is not available or subject to the constraints above.
637 boolean
638 get_obj_location(
639 struct obj *obj,
640 coordxy *xp, coordxy *yp,
641 int locflags)
643 switch (obj->where) {
644 case OBJ_INVENT:
645 *xp = u.ux;
646 *yp = u.uy;
647 return TRUE;
648 case OBJ_FLOOR:
649 *xp = obj->ox;
650 *yp = obj->oy;
651 return TRUE;
652 case OBJ_MINVENT:
653 if (obj->ocarry->mx) {
654 *xp = obj->ocarry->mx;
655 *yp = obj->ocarry->my;
656 return TRUE;
658 break; /* !mx => migrating monster */
659 case OBJ_BURIED:
660 if (locflags & BURIED_TOO) {
661 *xp = obj->ox;
662 *yp = obj->oy;
663 return TRUE;
665 break;
666 case OBJ_CONTAINED:
667 if (locflags & CONTAINED_TOO)
668 return get_obj_location(obj->ocontainer, xp, yp, locflags);
669 break;
671 *xp = *yp = 0;
672 return FALSE;
675 boolean
676 get_mon_location(
677 struct monst *mon,
678 coordxy *xp, coordxy *yp,
679 int locflags) /* non-zero means get location even if monster is buried */
681 if (mon == &gy.youmonst || (u.usteed && mon == u.usteed)) {
682 *xp = u.ux;
683 *yp = u.uy;
684 return TRUE;
685 } else if (mon->mx > 0 && (!mon->mburied || locflags)) {
686 *xp = mon->mx;
687 *yp = mon->my;
688 return TRUE;
689 } else { /* migrating or buried */
690 *xp = *yp = 0;
691 return FALSE;
695 /* used by revive() and animate_statue() */
696 struct monst *
697 montraits(
698 struct obj *obj,
699 coord *cc,
700 boolean adjacentok) /* False: at obj's spot only,
701 * True: nearby is allowed */
703 struct monst *mtmp = (struct monst *) 0,
704 *mtmp2 = has_omonst(obj) ? get_mtraits(obj, TRUE) : 0;
706 if (mtmp2) {
707 /* save_mtraits() validated mtmp2->mnum */
708 mtmp2->data = &mons[mtmp2->mnum];
710 if (mtmp2->mhpmax > 0 || is_rider(mtmp2->data)) {
711 mtmp = makemon(mtmp2->data, cc->x, cc->y,
712 (NO_MINVENT | MM_NOWAIT | MM_NOCOUNTBIRTH
713 /* in case mtmp2 is a long worm; saved traits for
714 long worm don't include tail segments so don't
715 give mtmp any; it will be given a new 'wormno'
716 though (unless those are exhausted) so be able
717 to grow new tail segments */
718 | MM_NOTAIL | MM_NOMSG
719 | (adjacentok ? MM_ADJACENTOK : 0)));
721 if (!mtmp) {
722 /* mtmp2 is a copy of obj's object->oextra->omonst extension
723 and is not on the map or on any monst lists */
724 dealloc_monst(mtmp2);
725 return (struct monst *) 0;
728 /* heal the monster; lower than normal level might come from
729 adj_lev() but we assume it has come from 'mtmp' being level
730 drained before finally killed; give a chance to restore
731 some levels so that trolls and Riders can't be drained to
732 level 0 and then trivially killed repeatedly */
733 if ((int) mtmp->m_lev < mtmp->data->mlevel) {
734 int ltmp = rnd(mtmp->data->mlevel + 1);
736 if (ltmp > (int) mtmp->m_lev) {
737 while ((int) mtmp->m_lev < ltmp) {
738 mtmp->m_lev++;
739 mtmp->mhpmax += monhp_per_lvl(mtmp);
741 mtmp2->m_lev = mtmp->m_lev;
744 if (mtmp->mhpmax > mtmp2->mhpmax) /* &&is_rider(mtmp2->data)*/
745 mtmp2->mhpmax = mtmp->mhpmax;
746 mtmp2->mhp = mtmp2->mhpmax;
747 /* Get these ones from mtmp */
748 mtmp2->minvent = mtmp->minvent; /*redundant*/
749 /* monster ID is available if the monster died in the current
750 game, but will be zero if the corpse was in a bones level
751 (we cleared it when loading bones) */
752 if (mtmp->m_id) {
753 mtmp2->m_id = mtmp->m_id;
754 /* might be bringing quest leader back to life */
755 if (svq.quest_status.leader_is_dead
756 /* leader_is_dead implies leader_m_id is valid */
757 && mtmp2->m_id == svq.quest_status.leader_m_id)
758 svq.quest_status.leader_is_dead = FALSE;
760 mtmp2->mx = mtmp->mx;
761 mtmp2->my = mtmp->my;
762 mtmp2->mux = mtmp->mux;
763 mtmp2->muy = mtmp->muy;
764 mtmp2->mw = mtmp->mw;
765 mtmp2->wormno = mtmp->wormno;
766 mtmp2->misc_worn_check = mtmp->misc_worn_check;
767 mtmp2->weapon_check = mtmp->weapon_check;
768 mtmp2->mtrapseen = mtmp->mtrapseen;
769 mtmp2->mflee = mtmp->mflee;
770 mtmp2->mburied = mtmp->mburied;
771 mtmp2->mundetected = mtmp->mundetected;
772 mtmp2->mfleetim = mtmp->mfleetim;
773 mtmp2->mlstmv = mtmp->mlstmv;
774 mtmp2->m_ap_type = mtmp->m_ap_type;
775 /* set these ones explicitly */
776 mtmp2->mrevived = 1;
777 mtmp2->mavenge = 0;
778 mtmp2->meating = 0;
779 mtmp2->mleashed = 0;
780 mtmp2->mtrapped = 0;
781 mtmp2->msleeping = 0;
782 mtmp2->mfrozen = 0;
783 mtmp2->mcanmove = 1;
784 /* most cancelled monsters return to normal,
785 but some need to stay cancelled */
786 if (!dmgtype(mtmp2->data, AD_SEDU)
787 && (!SYSOPT_SEDUCE || !dmgtype(mtmp2->data, AD_SSEX)))
788 mtmp2->mcan = 0;
789 mtmp2->mcansee = 1; /* set like in makemon */
790 mtmp2->mblinded = 0;
791 mtmp2->mstun = 0;
792 mtmp2->mconf = 0;
793 /* when traits are for a shopkeeper, dummy monster 'mtmp' won't
794 have necessary eshk data for replmon() -> replshk() */
795 if (mtmp2->isshk) {
796 neweshk(mtmp);
797 *ESHK(mtmp) = *ESHK(mtmp2);
798 if (ESHK(mtmp2)->bill_p != 0
799 && ESHK(mtmp2)->bill_p != (struct bill_x *) -1000)
800 ESHK(mtmp)->bill_p = &(ESHK(mtmp)->bill[0]);
801 mtmp->isshk = 1;
803 replmon(mtmp, mtmp2);
804 newsym(mtmp2->mx, mtmp2->my); /* Might now be invisible */
806 /* in case Protection_from_shape_changers is different
807 now than it was when the traits were stored */
808 restore_cham(mtmp2);
810 return mtmp2;
814 * get_container_location() returns the following information
815 * about the outermost container:
816 * loc argument gets set to:
817 * OBJ_INVENT if in hero's inventory; return 0.
818 * OBJ_FLOOR if on the floor; return 0.
819 * OBJ_BURIED if buried; return 0.
820 * OBJ_MINVENT if in monster's inventory; return monster.
821 * container_nesting is updated with the nesting depth of the containers
822 * if applicable.
824 struct monst *
825 get_container_location(
826 struct obj *obj,
827 int *loc,
828 int *container_nesting)
830 if (container_nesting)
831 *container_nesting = 0;
832 while (obj && obj->where == OBJ_CONTAINED) {
833 if (container_nesting)
834 *container_nesting += 1;
835 obj = obj->ocontainer;
837 if (obj) {
838 *loc = obj->where; /* outermost container's location */
839 if (obj->where == OBJ_MINVENT)
840 return obj->ocarry;
842 return (struct monst *) 0;
845 /* can zombie dig the location at x,y */
846 staticfn boolean
847 zombie_can_dig(coordxy x, coordxy y)
849 if (isok(x, y)) {
850 schar typ = levl[x][y].typ;
851 struct trap *ttmp;
853 if ((ttmp = t_at(x, y)) != 0)
854 return FALSE;
855 if (typ == ROOM || typ == CORR || typ == GRAVE)
856 return TRUE;
858 return FALSE;
862 * Attempt to revive the given corpse, return the revived monster if
863 * successful. Note: this does NOT use up the corpse if it fails.
864 * If corpse->quan is more than 1, only one corpse will be affected
865 * and only one monster will be resurrected.
867 struct monst *
868 revive(struct obj *corpse, boolean by_hero)
870 struct monst *mtmp = 0;
871 struct permonst *mptr;
872 struct obj *container;
873 coord xy;
874 coordxy x, y;
875 boolean one_of;
876 mmflags_nht mmflags = NO_MINVENT | MM_NOWAIT | MM_NOMSG;
877 int montype, cgend, container_nesting = 0;
878 boolean is_zomb;
880 if (corpse->otyp != CORPSE) {
881 impossible("Attempting to revive %s?", xname(corpse));
882 return (struct monst *) 0;
884 montype = corpse->corpsenm;
885 /* treat buried auto-reviver (troll, Rider?) like a zombie
886 so that it can dig itself out of the ground if it revives */
887 is_zomb = mons[montype].mlet == S_ZOMBIE
888 || (corpse->where == OBJ_BURIED && is_reviver(&mons[montype]));
890 /* if this corpse is being eaten, stop doing that; this should be done
891 after makemon() succeeds and skipped if it fails, but waiting until
892 we know the result for that would be too late */
893 cant_finish_meal(corpse);
895 x = y = 0;
896 if (corpse->where != OBJ_CONTAINED) {
897 int locflags = is_zomb ? BURIED_TOO : 0;
899 /* only for invent, minvent, or floor, or if zombie, buried */
900 container = 0;
901 (void) get_obj_location(corpse, &x, &y, locflags);
902 } else {
903 /* deal with corpses in [possibly nested] containers */
904 struct monst *carrier;
905 int holder = OBJ_FREE;
907 container = corpse->ocontainer;
908 carrier =
909 get_container_location(container, &holder, &container_nesting);
910 switch (holder) {
911 case OBJ_MINVENT:
912 x = carrier->mx, y = carrier->my;
913 break;
914 case OBJ_INVENT:
915 x = u.ux, y = u.uy;
916 break;
917 case OBJ_FLOOR:
918 (void) get_obj_location(corpse, &x, &y, CONTAINED_TOO);
919 break;
920 default:
921 break; /* x,y are 0 */
924 if (x) /* update corpse's location now that we're sure where it is */
925 corpse->ox = x, corpse->oy = y;
927 if (!x
928 /* Rules for revival from containers:
929 * - the container cannot be locked
930 * - the container cannot be heavily nested (>2 is arbitrary)
931 * - the container cannot be a statue or bag of holding
932 * (except in very rare cases for the latter)
934 || (container && (container->olocked || container_nesting > 2
935 || container->otyp == STATUE
936 || (container->otyp == BAG_OF_HOLDING && rn2(40))))
937 /* if buried zombie cannot dig itself out, do not revive */
938 || (is_zomb && corpse->where == OBJ_BURIED && !zombie_can_dig(x, y)))
939 return (struct monst *) 0;
941 /* prepare for the monster */
942 mptr = &mons[montype];
943 /* [should probably handle recorporealization first; if corpse and
944 ghost are at same location, revived creature shouldn't be bumped
945 to an adjacent spot by ghost which joins with it] */
946 if (MON_AT(x, y)) {
947 if (enexto(&xy, x, y, mptr))
948 x = xy.x, y = xy.y;
951 if (corpse->norevive
952 || (mons[montype].mlet == S_EEL && !IS_POOL(levl[x][y].typ))) {
953 if (cansee(x, y))
954 pline("%s twitches feebly.",
955 upstart(corpse_xname(corpse, (const char *) 0, CXN_PFX_THE)));
956 return (struct monst *) 0;
959 /* applicable when montraits/corpse->oextra->omonst aren't used */
960 cgend = (corpse->spe & CORPSTAT_GENDER);
961 if (cgend == CORPSTAT_MALE)
962 mmflags |= MM_MALE;
963 else if (cgend == CORPSTAT_FEMALE)
964 mmflags |= MM_FEMALE;
966 if (cant_revive(&montype, TRUE, corpse)) {
967 /* make a zombie or doppelganger instead */
968 /* note: montype has changed; mptr keeps old value for newcham() */
969 mtmp = makemon(&mons[montype], x, y, mmflags);
970 if (mtmp) {
971 /* skip ghost handling */
972 if (has_omid(corpse))
973 free_omid(corpse);
974 if (has_omonst(corpse))
975 free_omonst(corpse);
976 if (mtmp->cham == PM_DOPPELGANGER) {
977 /* change shape to match the corpse */
978 (void) newcham(mtmp, mptr, NO_NC_FLAGS);
979 } else if (mtmp->data->mlet == S_ZOMBIE) {
980 mtmp->mhp = mtmp->mhpmax = 100;
981 mon_adjust_speed(mtmp, 2, (struct obj *) 0); /* MFAST */
984 } else if (has_omonst(corpse)) {
985 /* use saved traits */
986 xy.x = x, xy.y = y;
987 mtmp = montraits(corpse, &xy, FALSE);
988 if (mtmp && mtmp->mtame && !mtmp->isminion)
989 wary_dog(mtmp, TRUE);
990 } else {
991 /* make a new monster */
992 mtmp = makemon(mptr, x, y, mmflags | MM_NOCOUNTBIRTH);
994 if (!mtmp)
995 return (struct monst *) 0;
997 /* hiders shouldn't already be re-hidden when they revive */
998 if (mtmp->mundetected) {
999 mtmp->mundetected = 0;
1000 newsym(mtmp->mx, mtmp->my);
1002 if (M_AP_TYPE(mtmp))
1003 seemimic(mtmp);
1005 one_of = (corpse->quan > 1L);
1006 if (one_of)
1007 corpse = splitobj(corpse, 1L);
1009 /* if this is caused by the hero there might be a shop charge */
1010 if (by_hero) {
1011 struct monst *shkp = 0;
1013 x = corpse->ox, y = corpse->oy;
1014 if (costly_spot(x, y)
1015 && (carried(corpse) ? corpse->unpaid : !corpse->no_charge))
1016 shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
1018 if (cansee(x, y)) {
1019 char buf[BUFSZ];
1021 Strcpy(buf, one_of ? "one of " : "");
1022 /* shk_your: "the " or "your " or "<mon>'s " or "<Shk>'s ".
1023 If the result is "Shk's " then it will be ambiguous:
1024 is Shk the mon carrying it, or does Shk's shop own it?
1025 Let's not worry about that... */
1026 (void) shk_your(eos(buf), corpse);
1027 if (one_of)
1028 corpse->quan++; /* force plural */
1029 Strcat(buf, corpse_xname(corpse, (const char *) 0, CXN_NO_PFX));
1030 if (one_of) /* could be simplified to ''corpse->quan = 1L;'' */
1031 corpse->quan--;
1032 pline("%s glows iridescently.", upstart(buf));
1033 iflags.last_msg = PLNMSG_OBJ_GLOWS; /* usually for BUC change */
1034 } else if (shkp) {
1035 /* need some prior description of the corpse since
1036 stolen_value() will refer to the object as "it" */
1037 pline("A corpse is resuscitated.");
1039 /* don't charge for shopkeeper's own corpse if we just revived him */
1040 if (shkp && mtmp != shkp)
1041 (void) stolen_value(corpse, x, y, (boolean) shkp->mpeaceful,
1042 FALSE);
1044 /* [we don't give any comparable message about the corpse for
1045 the !by_hero case because caller might have already done so] */
1048 /* handle recorporealization of an active ghost */
1049 if (has_omid(corpse)) {
1050 unsigned m_id;
1051 struct monst *ghost;
1052 struct obj *otmp;
1054 m_id = OMID(corpse);
1055 ghost = find_mid(m_id, FM_FMON);
1056 if (ghost && ghost->data == &mons[PM_GHOST]) {
1057 if (canseemon(ghost))
1058 pline("%s is suddenly drawn into its former body!",
1059 Monnam(ghost));
1060 /* transfer the ghost's inventory along with it */
1061 while ((otmp = ghost->minvent) != 0) {
1062 obj_extract_self(otmp);
1063 add_to_minv(mtmp, otmp);
1065 /* tame the revived monster if its ghost was tame */
1066 if (ghost->mtame && !mtmp->mtame) {
1067 if (tamedog(mtmp, (struct obj *) 0, FALSE)) {
1068 /* ghost's edog data is ignored */
1069 mtmp->mtame = ghost->mtame;
1072 /* was ghost, now alive, it's all very confusing */
1073 mtmp->mconf = 1;
1074 /* separate ghost monster no longer exists */
1075 mongone(ghost);
1077 free_omid(corpse);
1080 /* monster retains its name */
1081 if (has_oname(corpse) && !unique_corpstat(mtmp->data))
1082 mtmp = christen_monst(mtmp, ONAME(corpse));
1083 /* partially eaten corpse yields wounded monster */
1084 if (corpse->oeaten)
1085 mtmp->mhp = eaten_stat(mtmp->mhp, corpse);
1086 /* track that this monster was revived at least once */
1087 mtmp->mrevived = 1;
1089 /* finally, get rid of the corpse--it's gone now */
1090 switch (corpse->where) {
1091 case OBJ_INVENT:
1092 useup(corpse);
1093 break;
1094 case OBJ_FLOOR:
1095 /* not useupf(), which charges;
1096 delobj() won't use up a Rider's corpse, delobj_core(,TRUE) will */
1097 delobj_core(corpse, TRUE); /* for floor, also calls newsym() */
1098 break;
1099 case OBJ_MINVENT:
1100 m_useup(corpse->ocarry, corpse);
1101 break;
1102 case OBJ_CONTAINED:
1103 /* obj_extract_self() will update corpse->ocontainer->owt */
1104 obj_extract_self(corpse);
1105 obfree(corpse, (struct obj *) 0);
1106 break;
1107 case OBJ_BURIED:
1108 if (is_zomb) {
1109 obj_extract_self(corpse);
1110 obfree(corpse, (struct obj *) 0);
1111 break;
1113 FALLTHROUGH;
1114 /*FALLTHRU*/
1115 case OBJ_FREE:
1116 case OBJ_MIGRATING:
1117 case OBJ_ONBILL:
1118 case OBJ_LUAFREE:
1119 default:
1120 panic("revive default case %d", (int) corpse->where);
1123 return mtmp;
1126 staticfn void
1127 revive_egg(struct obj *obj) /* nonnull */
1130 * Note: generic eggs with corpsenm set to NON_PM will never hatch.
1132 if (obj->otyp != EGG)
1133 return;
1134 if (obj->corpsenm != NON_PM && !dead_species(obj->corpsenm, TRUE))
1135 attach_egg_hatch_timeout(obj, 0L);
1138 /* try to revive all corpses and eggs carried by `mon' */
1140 unturn_dead(struct monst *mon)
1142 struct obj *otmp, *otmp2;
1143 struct monst *mtmp2;
1144 char owner[BUFSZ], corpse[BUFSZ];
1145 unsigned save_norevive;
1146 boolean youseeit, different_type, is_u = (mon == &gy.youmonst);
1147 int corpsenm, res = 0;
1149 youseeit = is_u ? TRUE : canseemon(mon);
1150 otmp2 = is_u ? gi.invent : mon->minvent;
1151 owner[0] = corpse[0] = '\0'; /* lint suppression */
1153 while ((otmp = otmp2) != 0) {
1154 otmp2 = otmp->nobj;
1155 if (otmp->otyp == EGG)
1156 revive_egg(otmp);
1157 if (otmp->otyp != CORPSE)
1158 continue;
1159 /* save the name; the object is liable to go away */
1160 if (youseeit) {
1161 Strcpy(corpse, corpse_xname(otmp, (const char *) 0, CXN_NORMAL));
1162 /* shk_your/Shk_Your produces a value with a trailing space */
1163 if (otmp->quan > 1L) {
1164 Strcpy(owner, "One of ");
1165 (void) shk_your(eos(owner), otmp);
1166 } else
1167 (void) Shk_Your(owner, otmp);
1169 /* for a stack, only one is revived; if is_u, revive() calls
1170 useup() which calls update_inventory() but not encumber_msg() */
1171 corpsenm = otmp->corpsenm;
1172 /* norevive applies to revive timer, not to explicit unturn_dead() */
1173 save_norevive = otmp->norevive;
1174 otmp->norevive = 0;
1176 if ((mtmp2 = revive(otmp, !svc.context.mon_moving)) != 0) {
1177 ++res;
1178 /* might get revived as a zombie rather than corpse's monster */
1179 different_type = (mtmp2->data != &mons[corpsenm]);
1180 if (iflags.last_msg == PLNMSG_OBJ_GLOWS) {
1181 /* when hero zaps undead turning at self (or breaks
1182 non-empty wand), revive() reports "[one of] your <mon>
1183 corpse[s] glows iridescently"; override saved corpse
1184 and owner names to say "It comes alive" [note: we did
1185 earlier setup because corpse gets used up but need to
1186 do the override here after revive() sets 'last_msg'] */
1187 Strcpy(corpse, "It");
1188 owner[0] = '\0';
1190 if (youseeit)
1191 pline("%s%s suddenly %s%s%s!", owner, corpse,
1192 nonliving(mtmp2->data) ? "reanimates" : "comes alive",
1193 different_type ? " as " : "",
1194 different_type ? an(mon_pmname(mtmp2)) : "");
1195 else if (canseemon(mtmp2))
1196 pline("%s suddenly appears!", Amonnam(mtmp2));
1197 } else {
1198 /* revival failed; corpse 'otmp' is intact */
1199 otmp->norevive = save_norevive ? 1 : 0;
1202 if (is_u && res)
1203 (void) encumber_msg();
1205 return res;
1208 void
1209 unturn_you(void)
1211 (void) unturn_dead(&gy.youmonst); /* hit carried corpses and eggs */
1213 if (is_undead(gy.youmonst.data)) {
1214 You_feel("frightened and %sstunned.", Stunned ? "even more " : "");
1215 make_stunned((HStun & TIMEOUT) + (long) rnd(30), FALSE);
1216 } else {
1217 You("shudder in dread.");
1221 /* cancel obj, possibly carried by you or a monster */
1222 void
1223 cancel_item(struct obj *obj)
1225 int otyp = obj->otyp;
1227 if (carried(obj)) {
1228 /* handle items being worn by hero */
1229 switch (otyp) {
1230 case RIN_GAIN_STRENGTH:
1231 if ((obj->owornmask & W_RING) != 0L) {
1232 ABON(A_STR) -= obj->spe;
1233 disp.botl = TRUE;
1235 break;
1236 case RIN_GAIN_CONSTITUTION:
1237 if ((obj->owornmask & W_RING) != 0L) {
1238 ABON(A_CON) -= obj->spe;
1239 disp.botl = TRUE;
1241 break;
1242 case RIN_ADORNMENT:
1243 if ((obj->owornmask & W_RING) != 0L) {
1244 ABON(A_CHA) -= obj->spe;
1245 disp.botl = TRUE;
1247 break;
1248 case RIN_INCREASE_ACCURACY:
1249 if ((obj->owornmask & W_RING) != 0L)
1250 u.uhitinc -= obj->spe;
1251 break;
1252 case RIN_INCREASE_DAMAGE:
1253 if ((obj->owornmask & W_RING) != 0L)
1254 u.udaminc -= obj->spe;
1255 break;
1256 case RIN_PROTECTION:
1257 if ((obj->owornmask & W_RING) != 0L)
1258 disp.botl = TRUE;
1259 break;
1260 case GAUNTLETS_OF_DEXTERITY:
1261 if ((obj->owornmask & W_ARMG) != 0L) {
1262 ABON(A_DEX) -= obj->spe;
1263 disp.botl = TRUE;
1265 break;
1266 case HELM_OF_BRILLIANCE:
1267 if ((obj->owornmask & W_ARMH) != 0L) {
1268 ABON(A_INT) -= obj->spe;
1269 ABON(A_WIS) -= obj->spe;
1270 disp.botl = TRUE;
1272 break;
1273 default:
1274 if ((obj->owornmask & W_ARMOR) != 0L) /* AC */
1275 disp.botl = TRUE;
1276 break;
1279 /* cancelled item might not be in hero's possession but
1280 cancellation is presumed to be instigated by hero */
1281 if (objects[otyp].oc_magic
1282 || (obj->spe && (obj->oclass == ARMOR_CLASS
1283 || obj->oclass == WEAPON_CLASS || is_weptool(obj)))
1284 || otyp == POT_ACID
1285 || otyp == POT_SICKNESS
1286 || (otyp == POT_WATER && (obj->blessed || obj->cursed))
1287 /* not magic; cancels to blank spellbook */
1288 || otyp == SPE_NOVEL) {
1289 int cancelled_spe = (obj->oclass == WAND_CLASS
1290 || otyp == CRYSTAL_BALL) ? -1 : 0;
1292 if (obj->spe != cancelled_spe
1293 && otyp != WAN_CANCELLATION /* can't cancel cancellation */
1294 && otyp != MAGIC_LAMP /* cancelling doesn't remove djinni */
1295 && otyp != CANDELABRUM_OF_INVOCATION) {
1296 costly_alteration(obj, COST_CANCEL);
1297 obj->spe = cancelled_spe;
1299 switch (obj->oclass) {
1300 case SCROLL_CLASS:
1301 costly_alteration(obj, COST_CANCEL);
1302 obj->otyp = SCR_BLANK_PAPER;
1303 obj->spe = 0;
1304 break;
1305 case SPBOOK_CLASS:
1306 if (otyp != SPE_CANCELLATION && otyp != SPE_BOOK_OF_THE_DEAD) {
1307 costly_alteration(obj, COST_CANCEL);
1308 obj->otyp = SPE_BLANK_PAPER;
1309 /* cancelling a novel is more involved than a spellbook */
1310 if (otyp == SPE_NOVEL) /* old type */
1311 blank_novel(obj);
1313 break;
1314 case POTION_CLASS:
1315 costly_alteration(obj, (otyp != POT_WATER) ? COST_CANCEL
1316 : obj->cursed ? COST_UNCURS : COST_UNBLSS);
1317 if (otyp == POT_SICKNESS || otyp == POT_SEE_INVISIBLE) {
1318 /* sickness is "biologically contaminated" fruit juice;
1319 cancel it and it just becomes fruit juice...
1320 whereas see invisible tastes like "enchanted" fruit
1321 juice, it similarly cancels */
1322 obj->otyp = POT_FRUIT_JUICE;
1323 } else {
1324 obj->otyp = POT_WATER;
1325 obj->odiluted = 0; /* same as any other water */
1327 break;
1330 /* cancelling a troll's corpse prevents it from reviving (on its own;
1331 does not affect undead turning induced revival) */
1332 if (obj->otyp == CORPSE && obj->timed
1333 && !is_rider(&mons[obj->corpsenm])) {
1334 anything a = *obj_to_any(obj);
1335 long timout = peek_timer(REVIVE_MON, &a);
1337 if (timout) {
1338 (void) stop_timer(REVIVE_MON, &a);
1339 (void) start_timer(timout, TIMER_OBJECT, ROT_CORPSE, &a);
1343 unbless(obj);
1344 uncurse(obj);
1345 return;
1348 /* soaking or cancelling a novel converts it into a blank spellbook but
1349 needs more than just changing its otyp (caller is responsible for that) */
1350 void
1351 blank_novel(struct obj *obj)
1353 assert(obj->otyp == SPE_BLANK_PAPER);
1354 /* novelidx overloads corpsenm, not used for spellbooks */
1355 obj->novelidx = 0;
1356 free_oname(obj); /* get rid of [former] novel's title */
1357 /* a blank spellbook weighs more than a novel; update obj's weight and
1358 recursively the weight of any container holding it */
1359 container_weight(obj);
1362 /* Remove a positive enchantment or charge from obj,
1363 * possibly carried by you or a monster
1365 boolean
1366 drain_item(struct obj *obj, boolean by_you)
1368 boolean u_ring;
1370 /* Is this a charged/enchanted object? */
1371 if (!obj
1372 || (!objects[obj->otyp].oc_charged && obj->oclass != WEAPON_CLASS
1373 && obj->oclass != ARMOR_CLASS && !is_weptool(obj))
1374 || obj->spe <= 0)
1375 return FALSE;
1376 if (defends(AD_DRLI, obj) || defends_when_carried(AD_DRLI, obj)
1377 || obj_resists(obj, 10, 90))
1378 return FALSE;
1380 /* Charge for the cost of the object */
1381 if (by_you)
1382 costly_alteration(obj, COST_DRAIN);
1384 /* Drain the object and any implied effects */
1385 obj->spe--;
1386 u_ring = (obj == uleft) || (obj == uright);
1387 switch (obj->otyp) {
1388 case RIN_GAIN_STRENGTH:
1389 if ((obj->owornmask & W_RING) && u_ring) {
1390 ABON(A_STR)--;
1391 disp.botl = TRUE;
1393 break;
1394 case RIN_GAIN_CONSTITUTION:
1395 if ((obj->owornmask & W_RING) && u_ring) {
1396 ABON(A_CON)--;
1397 disp.botl = TRUE;
1399 break;
1400 case RIN_ADORNMENT:
1401 if ((obj->owornmask & W_RING) && u_ring) {
1402 ABON(A_CHA)--;
1403 disp.botl = TRUE;
1405 break;
1406 case RIN_INCREASE_ACCURACY:
1407 if ((obj->owornmask & W_RING) && u_ring)
1408 u.uhitinc--;
1409 break;
1410 case RIN_INCREASE_DAMAGE:
1411 if ((obj->owornmask & W_RING) && u_ring)
1412 u.udaminc--;
1413 break;
1414 case RIN_PROTECTION:
1415 if (u_ring)
1416 disp.botl = TRUE; /* bot() will recalc u.uac */
1417 break;
1418 case HELM_OF_BRILLIANCE:
1419 if ((obj->owornmask & W_ARMH) && (obj == uarmh)) {
1420 ABON(A_INT)--;
1421 ABON(A_WIS)--;
1422 disp.botl = TRUE;
1424 break;
1425 case GAUNTLETS_OF_DEXTERITY:
1426 if ((obj->owornmask & W_ARMG) && (obj == uarmg)) {
1427 ABON(A_DEX)--;
1428 disp.botl = TRUE;
1430 break;
1431 default:
1432 break;
1434 if (disp.botl)
1435 bot();
1436 if (carried(obj))
1437 update_inventory();
1438 return TRUE;
1441 boolean
1442 obj_resists(struct obj *obj,
1443 int ochance, /* percent chance for ordinary objects */
1444 int achance) /* percent chance for artifacts */
1446 if (obj->otyp == AMULET_OF_YENDOR
1447 || obj->otyp == SPE_BOOK_OF_THE_DEAD
1448 || obj->otyp == CANDELABRUM_OF_INVOCATION
1449 || obj->otyp == BELL_OF_OPENING
1450 || (obj->otyp == CORPSE && is_rider(&mons[obj->corpsenm]))) {
1451 return TRUE;
1452 } else {
1453 int chance = rn2(100);
1455 return (boolean) (chance < (obj->oartifact ? achance : ochance));
1459 boolean
1460 obj_shudders(struct obj *obj)
1462 int zap_odds;
1464 if (svc.context.bypasses && obj->bypass)
1465 return FALSE;
1467 if (obj->oclass == WAND_CLASS)
1468 zap_odds = 3; /* half-life = 2 zaps */
1469 else if (obj->cursed)
1470 zap_odds = 3; /* half-life = 2 zaps */
1471 else if (obj->blessed)
1472 zap_odds = 12; /* half-life = 8 zaps */
1473 else
1474 zap_odds = 8; /* half-life = 6 zaps */
1476 /* adjust for "large" quantities of identical things */
1477 if (obj->quan > 4L)
1478 zap_odds /= 2;
1480 return (boolean) !rn2(zap_odds);
1483 /* Use up at least minwt number of things made of material mat.
1484 * There's also a chance that other stuff will be used up. Finally,
1485 * there's a random factor here to keep from always using the stuff
1486 * at the top of the pile.
1488 staticfn void
1489 polyuse(struct obj *objhdr, int mat, int minwt)
1491 struct obj *otmp, *otmp2;
1493 for (otmp = objhdr; minwt > 0 && otmp; otmp = otmp2) {
1494 otmp2 = otmp->nexthere;
1495 if (svc.context.bypasses && otmp->bypass)
1496 continue;
1497 if (otmp == uball || otmp == uchain)
1498 continue;
1499 if (obj_resists(otmp, 0, 0))
1500 continue; /* preserve unique objects */
1501 #ifdef MAIL_STRUCTURES
1502 if (otmp->otyp == SCR_MAIL)
1503 continue;
1504 #endif
1506 if (((int) objects[otmp->otyp].oc_material == mat)
1507 == (rn2(minwt + 1) != 0)) {
1508 /* appropriately add damage to bill */
1509 if (costly_spot(otmp->ox, otmp->oy)) {
1510 if (*u.ushops)
1511 addtobill(otmp, FALSE, FALSE, FALSE);
1512 else
1513 (void) stolen_value(otmp, otmp->ox, otmp->oy, FALSE,
1514 FALSE);
1516 if (otmp->quan < LARGEST_INT)
1517 minwt -= (int) otmp->quan;
1518 else
1519 minwt = 0;
1520 delobj(otmp);
1526 * Polymorph some of the stuff in this pile into a monster, preferably
1527 * a golem of the kind okind.
1529 staticfn void
1530 create_polymon(struct obj *obj, int okind)
1532 struct permonst *mdat = (struct permonst *) 0;
1533 struct monst *mtmp;
1534 const char *material;
1535 int pm_index;
1537 if (svc.context.bypasses) {
1538 /* this is approximate because the "no golems" !obj->nexthere
1539 check below doesn't understand bypassed objects; but it
1540 should suffice since bypassed objects always end up as a
1541 consecutive group at the top of their pile */
1542 while (obj && obj->bypass)
1543 obj = obj->nexthere;
1546 /* no golems if you zap only one object -- not enough stuff */
1547 if (!obj || (!obj->nexthere && obj->quan == 1L))
1548 return;
1550 /* some of these choices are arbitrary */
1551 switch (okind) {
1552 case IRON:
1553 case METAL:
1554 case MITHRIL:
1555 pm_index = PM_IRON_GOLEM;
1556 material = "metal ";
1557 break;
1558 case COPPER:
1559 case SILVER:
1560 case PLATINUM:
1561 case GEMSTONE:
1562 case MINERAL:
1563 pm_index = rn2(2) ? PM_STONE_GOLEM : PM_CLAY_GOLEM;
1564 material = "lithic ";
1565 break;
1566 case 0:
1567 case FLESH:
1568 /* there is no flesh type, but all food is type 0, so we use it */
1569 pm_index = PM_FLESH_GOLEM;
1570 material = "organic ";
1571 break;
1572 case WOOD:
1573 pm_index = PM_WOOD_GOLEM;
1574 material = "wood ";
1575 break;
1576 case LEATHER:
1577 pm_index = PM_LEATHER_GOLEM;
1578 material = "leather ";
1579 break;
1580 case CLOTH:
1581 pm_index = PM_ROPE_GOLEM;
1582 material = "cloth ";
1583 break;
1584 case BONE:
1585 pm_index = PM_SKELETON; /* nearest thing to "bone golem" */
1586 material = "bony ";
1587 break;
1588 case GOLD:
1589 pm_index = PM_GOLD_GOLEM;
1590 material = "gold ";
1591 break;
1592 case GLASS:
1593 pm_index = PM_GLASS_GOLEM;
1594 material = "glassy ";
1595 break;
1596 case PAPER:
1597 pm_index = PM_PAPER_GOLEM;
1598 material = "paper ";
1599 break;
1600 default:
1601 /* if all else fails... */
1602 pm_index = PM_STRAW_GOLEM;
1603 material = "";
1604 break;
1607 if (!(svm.mvitals[pm_index].mvflags & G_GENOD))
1608 mdat = &mons[pm_index];
1610 mtmp = makemon(mdat, obj->ox, obj->oy, MM_NOMSG);
1611 polyuse(obj, okind, (int) mons[pm_index].cwt);
1613 if (mtmp && cansee(mtmp->mx, mtmp->my)) {
1614 pline("Some %sobjects meld, and %s arises from the pile!", material,
1615 a_monnam(mtmp));
1619 /* Assumes obj is on the floor. */
1620 void
1621 do_osshock(struct obj *obj)
1623 long i;
1625 #ifdef MAIL_STRUCTURES
1626 if (obj->otyp == SCR_MAIL)
1627 return;
1628 #endif
1629 go.obj_zapped = TRUE;
1631 if (gp.poly_zapped < 0) {
1632 /* some may metamorphosize */
1633 for (i = obj->quan; i; i--)
1634 if (!rn2(Luck + 45)) {
1635 gp.poly_zapped = objects[obj->otyp].oc_material;
1636 break;
1640 /* if quan > 1 then some will survive intact */
1641 if (obj->quan > 1L) {
1642 if (obj->quan > LARGEST_INT)
1643 obj = splitobj(obj, (long) rnd(30000));
1644 else
1645 obj = splitobj(obj, (long) rnd((int) obj->quan - 1));
1648 /* appropriately add damage to bill */
1649 if (costly_spot(obj->ox, obj->oy)) {
1650 if (*u.ushops)
1651 addtobill(obj, FALSE, FALSE, FALSE);
1652 else
1653 (void) stolen_value(obj, obj->ox, obj->oy, FALSE, FALSE);
1656 /* zap the object */
1657 delobj(obj);
1660 /* Returns TRUE if obj resists polymorphing */
1661 boolean
1662 obj_unpolyable(struct obj *obj)
1664 return (unpolyable(obj)
1665 || obj == uball || obj == uskin
1666 || obj_resists(obj, 5, 95));
1669 /* classes of items whose current charge count carries over across polymorph
1671 staticfn const char charged_objs[] = { WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS,
1672 '\0' };
1675 * Polymorph the object to the given object ID. If the ID is STRANGE_OBJECT
1676 * then pick random object from the source's class (this is the standard
1677 * "polymorph" case). If ID is set to a specific object, inhibit fusing
1678 * n objects into 1. This could have been added as a flag, but currently
1679 * it is tied to not being the standard polymorph case. The new polymorphed
1680 * object replaces obj in its link chains. Return value is a pointer to
1681 * the new object.
1683 * This should be safe to call for an object anywhere.
1685 struct obj *
1686 poly_obj(struct obj *obj, int id)
1688 struct obj *otmp;
1689 coordxy ox = 0, oy = 0;
1690 long old_wornmask, new_wornmask = 0L;
1691 boolean can_merge = (id == STRANGE_OBJECT);
1692 int obj_location = obj->where;
1694 if (obj->otyp == BOULDER)
1695 sokoban_guilt();
1696 if (id == STRANGE_OBJECT) { /* preserve symbol */
1697 int try_limit = 3;
1698 unsigned magic_obj = objects[obj->otyp].oc_magic;
1700 if (obj->otyp == UNICORN_HORN && obj->degraded_horn)
1701 magic_obj = 0;
1702 /* Try up to 3 times to make the magic-or-not status of
1703 the new item be the same as it was for the old one. */
1704 otmp = (struct obj *) 0;
1705 do {
1706 if (otmp)
1707 delobj(otmp);
1708 otmp = mkobj(obj->oclass, FALSE);
1709 } while (--try_limit > 0
1710 && objects[otmp->otyp].oc_magic != magic_obj);
1711 } else {
1712 /* literally replace obj with this new thing */
1713 otmp = mksobj(id, FALSE, FALSE);
1714 /* Actually more things use corpsenm but they polymorph differently */
1715 #define USES_CORPSENM(typ) \
1716 ((typ) == CORPSE || (typ) == STATUE || (typ) == FIGURINE)
1718 if (USES_CORPSENM(obj->otyp) && USES_CORPSENM(id))
1719 set_corpsenm(otmp, obj->corpsenm);
1720 #undef USES_CORPSENM
1723 /* preserve quantity */
1724 otmp->quan = obj->quan;
1725 /* preserve the shopkeepers (lack of) interest */
1726 otmp->no_charge = obj->no_charge;
1727 /* preserve inventory letter if in inventory */
1728 if (obj_location == OBJ_INVENT)
1729 otmp->invlet = obj->invlet;
1730 #ifdef MAIL_STRUCTURES
1731 /* You can't send yourself 100 mail messages and then
1732 * polymorph them into useful scrolls
1734 if (obj->otyp == SCR_MAIL) {
1735 otmp->otyp = SCR_MAIL;
1736 otmp->spe = 1;
1738 #endif
1740 /* avoid abusing eggs laid by you */
1741 if (obj->otyp == EGG && obj->spe) {
1742 int mnum, tryct = 100;
1744 /* first, turn into a generic egg */
1745 if (otmp->otyp == EGG)
1746 kill_egg(otmp);
1747 else {
1748 otmp->otyp = EGG;
1749 otmp->owt = weight(otmp);
1751 otmp->corpsenm = NON_PM;
1752 otmp->spe = 0;
1754 /* now change it into something laid by the hero */
1755 while (tryct--) {
1756 mnum = can_be_hatched(random_monster(rn2));
1757 if (mnum != NON_PM && !dead_species(mnum, TRUE)) {
1758 otmp->spe = 1; /* laid by hero */
1759 set_corpsenm(otmp, mnum); /* also sets hatch timer */
1760 break;
1765 /* keep special fields (including charges on wands) */
1766 if (strchr(charged_objs, otmp->oclass))
1767 otmp->spe = obj->spe;
1768 otmp->recharged = obj->recharged;
1770 otmp->cursed = obj->cursed;
1771 otmp->blessed = obj->blessed;
1773 if (erosion_matters(otmp)) {
1774 if (is_flammable(otmp) || is_rustprone(otmp) || is_crackable(otmp))
1775 otmp->oeroded = obj->oeroded;
1776 if (is_corrodeable(otmp) || is_rottable(otmp))
1777 otmp->oeroded2 = obj->oeroded2;
1778 if (is_damageable(otmp))
1779 otmp->oerodeproof = obj->oerodeproof;
1782 /* Keep chest/box traps and poisoned ammo if we may */
1783 if (obj->otrapped && Is_box(otmp))
1784 otmp->otrapped = 1;
1785 if (obj->opoisoned && is_poisonable(otmp))
1786 otmp->opoisoned = 1;
1788 if (id == STRANGE_OBJECT && obj->otyp == CORPSE) {
1789 /* turn crocodile corpses into shoes */
1790 if (obj->corpsenm == PM_CROCODILE) {
1791 otmp->otyp = LOW_BOOTS;
1792 otmp->oclass = ARMOR_CLASS;
1793 otmp->spe = 0;
1794 otmp->oeroded = 0;
1795 otmp->oerodeproof = TRUE;
1796 otmp->quan = 1L;
1797 otmp->cursed = FALSE;
1800 if (obj->otyp == LEASH && obj->leashmon != 0) {
1801 if (otmp->otyp == LEASH) {
1802 otmp->leashmon = obj->leashmon;
1803 /* clear m_id before delobj(), to avoid o_unleash() by obfree() */
1804 obj->leashmon = 0;
1805 } else {
1806 /* obfree() would do this if we didn't do it here */
1807 o_unleash(obj);
1811 /* no box contents --KAA */
1812 if (Has_contents(otmp))
1813 delete_contents(otmp);
1815 /* 'n' merged objects may be fused into 1 object */
1816 if (otmp->quan > 1L && (!objects[otmp->otyp].oc_merge
1817 || (can_merge && otmp->quan > (long) rn2(1000))))
1818 otmp->quan = 1L;
1820 switch (otmp->oclass) {
1821 case TOOL_CLASS:
1822 if (otmp->otyp == MAGIC_LAMP) {
1823 otmp->otyp = OIL_LAMP;
1824 otmp->age = 1500L; /* "best" oil lamp possible */
1825 } else if (otmp->otyp == MAGIC_MARKER) {
1826 otmp->recharged = 1; /* degraded quality */
1828 /* don't care about the recharge count of other tools */
1829 break;
1831 case WAND_CLASS:
1832 while (otmp->otyp == WAN_WISHING || otmp->otyp == WAN_POLYMORPH)
1833 otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING);
1834 /* altering the object tends to degrade its quality
1835 (analogous to spellbook `read count' handling) */
1836 if ((int) otmp->recharged < rn2(7)) /* recharge_limit */
1837 otmp->recharged++;
1838 break;
1840 case POTION_CLASS:
1841 while (otmp->otyp == POT_POLYMORPH)
1842 otmp->otyp = rnd_class(POT_GAIN_ABILITY, POT_WATER);
1843 /* potions of oil use obj->age field differently from other potions */
1844 if (otmp->otyp == POT_OIL || obj->otyp == POT_OIL)
1845 fixup_oil(otmp, obj);
1846 break;
1848 case SPBOOK_CLASS:
1849 while (otmp->otyp == SPE_POLYMORPH)
1850 otmp->otyp = rnd_class(svb.bases[SPBOOK_CLASS], SPE_BLANK_PAPER);
1851 /* reduce spellbook abuse; non-blank books degrade;
1852 3.7: novels don't use spestudied so shouldn't degrade to blank
1853 (but don't force spestudied to zero for them since a non-zero
1854 value could get passed along to a future polymorph) */
1855 if (otmp->otyp != SPE_BLANK_PAPER && otmp->otyp != SPE_NOVEL) {
1856 otmp->spestudied = obj->spestudied + 1;
1857 if (otmp->spestudied > MAX_SPELL_STUDY) {
1858 otmp->otyp = SPE_BLANK_PAPER;
1859 /* writing a new book over it will yield an unstudied
1860 one; re-polymorphing this one as-is may or may not
1861 get something non-blank */
1862 otmp->spestudied = rn2(otmp->spestudied);
1865 break;
1867 case GEM_CLASS:
1868 if (otmp->quan > (long) rnd(4)
1869 && objects[obj->otyp].oc_material == MINERAL
1870 && objects[otmp->otyp].oc_material != MINERAL) {
1871 otmp->otyp = ROCK; /* transmutation backfired */
1872 otmp->quan /= 2L; /* some material has been lost */
1874 break;
1877 /* update the weight */
1878 otmp->owt = weight(otmp);
1881 * ** we are now done adjusting the object (except possibly wearing it) **
1884 (void) get_obj_location(obj, &ox, &oy, BURIED_TOO | CONTAINED_TOO);
1885 old_wornmask = obj->owornmask & ~(W_ART | W_ARTI);
1886 /* swap otmp for obj */
1887 replace_object(obj, otmp);
1888 if (obj_location == OBJ_INVENT) {
1890 * We may need to do extra adjustments for the hero if we're
1891 * messing with the hero's inventory. The following calls are
1892 * equivalent to calling freeinv() on obj and addinv_nomerge()
1893 * on otmp, while doing an in-place swap of the actual objects.
1895 freeinv_core(obj);
1896 addinv_core1(otmp);
1897 addinv_core2(otmp);
1899 * Handle polymorph of worn item. Stone-to-flesh cast on self can
1900 * affect multiple objects at once, but their new forms won't
1901 * produce any side-effects. A single worn item dipped into potion
1902 * of polymorph can produce side-effects but those won't yield out
1903 * of sequence messages because current polymorph is finished.
1905 if (old_wornmask) {
1906 boolean was_twohanded = bimanual(obj), was_twoweap = u.twoweap;
1908 /* wearslot() expects us to deal with wielded/alt-wep/quivered
1909 items in case they're not weapons; for other slots it might
1910 return multiple bits (ring left|right); narrow that down to
1911 the bit(s) currently in use */
1912 new_wornmask = ((old_wornmask & W_WEAPONS) != 0L) ? old_wornmask
1913 : (wearslot(otmp) & old_wornmask);
1914 remove_worn_item(obj, TRUE);
1915 /* if the new form can be worn in the same slot, make it so */
1916 if ((new_wornmask & W_WEP) != 0L) {
1917 if (was_twohanded || !bimanual(otmp) || !uarms)
1918 setuwep(otmp);
1919 if (was_twoweap && uwep && !bimanual(uwep))
1920 set_twoweap(TRUE); /* u.twoweap = TRUE */
1921 } else if ((new_wornmask & W_SWAPWEP) != 0L) {
1922 if (was_twohanded || !bimanual(otmp))
1923 setuswapwep(otmp);
1924 if (was_twoweap && uswapwep)
1925 set_twoweap(TRUE); /* u.twoweap = TRUE */
1926 } else if ((new_wornmask & W_QUIVER) != 0L) {
1927 setuqwep(otmp);
1928 } else if (new_wornmask) {
1929 setworn(otmp, new_wornmask);
1930 /* set_wear() might result in otmp being destroyed if
1931 worn amulet has been turned into an amulet of change */
1932 set_wear(otmp);
1933 otmp = wearmask_to_obj(new_wornmask); /* might be Null */
1935 } /* old_wornmask */
1936 } else if (obj_location == OBJ_FLOOR) {
1937 if (obj->otyp == BOULDER && otmp->otyp != BOULDER) {
1938 if (!does_block(ox, oy, &levl[ox][oy]))
1939 unblock_point(ox, oy);
1940 } else if (obj->otyp != BOULDER && otmp->otyp == BOULDER) {
1941 /* leaving boulder in liquid would trigger sanity_check warning */
1942 if (is_pool_or_lava(ox, oy))
1943 fracture_rock(otmp);
1944 if (does_block(ox, oy, &levl[ox][oy]))
1945 block_point(ox, oy);
1949 /* note: if otmp is gone, billing for it was handled by useup() */
1950 if (((otmp && !carried(otmp)) || obj->unpaid) && costly_spot(ox, oy)) {
1951 struct monst *shkp = shop_keeper(*in_rooms(ox, oy, SHOPBASE));
1953 if ((!obj->no_charge
1954 || (Has_contents(obj)
1955 && (contained_cost(obj, shkp, 0L, FALSE, FALSE) != 0L)))
1956 && inhishop(shkp)) {
1957 if (shkp->mpeaceful) {
1958 if (*u.ushops
1959 && (*in_rooms(u.ux, u.uy, 0)
1960 == *in_rooms(shkp->mx, shkp->my, 0))
1961 && !costly_spot(u.ux, u.uy)) {
1962 make_angry_shk(shkp, ox, oy);
1963 } else {
1964 pline("%s gets angry!", Shknam(shkp));
1965 hot_pursuit(shkp);
1967 } else
1968 Norep("%s is furious!", Shknam(shkp));
1971 delobj(obj);
1972 return otmp;
1975 /* stone-to-flesh spell hits and maybe transforms or animates obj */
1976 staticfn int
1977 stone_to_flesh_obj(struct obj *obj) /* nonnull */
1979 struct permonst *ptr;
1980 struct monst *mon, *shkp;
1981 struct obj *item;
1982 coordxy oox, ooy;
1983 boolean smell = FALSE, golem_xform = FALSE;
1984 int res = 1; /* affected object by default */
1986 if (objects[obj->otyp].oc_material != MINERAL
1987 && objects[obj->otyp].oc_material != GEMSTONE)
1988 return 0;
1989 /* Heart of Ahriman usually resists; ordinary items rarely do */
1990 if (obj_resists(obj, 2, 98))
1991 return 0;
1993 (void) get_obj_location(obj, &oox, &ooy, 0);
1994 /* add more if stone objects are added.. */
1995 switch (objects[obj->otyp].oc_class) {
1996 case ROCK_CLASS: /* boulders and statues */
1997 case TOOL_CLASS: /* figurines */
1998 if (obj->otyp == BOULDER) {
1999 obj = poly_obj(obj, ENORMOUS_MEATBALL);
2000 smell = TRUE;
2001 } else if (obj->otyp == STATUE || obj->otyp == FIGURINE) {
2002 ptr = &mons[obj->corpsenm];
2003 if (is_golem(ptr)) {
2004 golem_xform = (ptr != &mons[PM_FLESH_GOLEM]);
2005 } else if (vegetarian(ptr)) {
2006 /* Don't animate monsters that aren't flesh */
2007 obj = poly_obj(obj, MEATBALL);
2008 smell = TRUE;
2009 break;
2011 if (obj->otyp == STATUE) {
2012 /* animate_statue() forces all golems to become flesh golems */
2013 mon = animate_statue(obj, oox, ooy, ANIMATE_SPELL, (int *) 0);
2014 } else { /* (obj->otyp == FIGURINE) */
2015 if (golem_xform)
2016 ptr = &mons[PM_FLESH_GOLEM];
2017 mon = makemon(ptr, oox, ooy, NO_MINVENT|MM_NOMSG);
2018 if (mon) {
2019 if (costly_spot(oox, ooy)
2020 && (carried(obj) ? obj->unpaid : !obj->no_charge)) {
2021 shkp = shop_keeper(*in_rooms(oox, ooy, SHOPBASE));
2022 stolen_value(obj, oox, ooy,
2023 (shkp && shkp->mpeaceful), FALSE);
2025 if (obj->timed)
2026 obj_stop_timers(obj);
2027 if (carried(obj))
2028 useup(obj);
2029 else
2030 delobj(obj);
2031 if (cansee(mon->mx, mon->my))
2032 pline_The("figurine %sanimates!",
2033 golem_xform ? "turns to flesh and " : "");
2036 if (mon) {
2037 ptr = mon->data;
2038 /* this golem handling is redundant... */
2039 if (is_golem(ptr) && ptr != &mons[PM_FLESH_GOLEM])
2040 (void) newcham(mon, &mons[PM_FLESH_GOLEM],
2041 NC_VIA_WAND_OR_SPELL);
2042 } else if ((ptr->geno & (G_NOCORPSE | G_UNIQ)) != 0) {
2043 /* didn't revive but can't leave corpse either */
2044 res = 0;
2045 } else {
2046 /* unlikely to get here since genociding monsters also
2047 sets the G_NOCORPSE flag; drop statue's contents */
2048 while ((item = obj->cobj) != 0) {
2049 bypass_obj(item); /* make stone-to-flesh miss it */
2050 obj_extract_self(item);
2051 place_object(item, oox, ooy);
2053 obj = poly_obj(obj, CORPSE);
2055 } else { /* miscellaneous tool or unexpected rock... */
2056 res = 0;
2058 break;
2059 /* maybe add weird things to become? */
2060 case RING_CLASS: /* some of the rings are stone */
2061 obj = poly_obj(obj, MEAT_RING);
2062 smell = TRUE;
2063 break;
2064 case WAND_CLASS: /* marble wand */
2065 obj = poly_obj(obj, MEAT_STICK);
2066 smell = TRUE;
2067 break;
2068 case GEM_CLASS: /* stones & gems */
2069 obj = poly_obj(obj, MEATBALL);
2070 smell = TRUE;
2071 break;
2072 case WEAPON_CLASS: /* crysknife */
2073 FALLTHROUGH;
2074 /*FALLTHRU*/
2075 default:
2076 res = 0;
2077 break;
2079 nhUse(obj); /* avoid 'assigned value not used' for poly_obj() calls */
2081 if (smell) {
2082 /* non-meat eaters smell meat, meat eaters smell its flavor;
2083 monks are considered non-meat eaters regardless of behavior;
2084 other roles are non-meat eaters if they haven't broken
2085 vegetarian conduct yet (or if poly'd into non-carnivorous/
2086 non-omnivorous form, regardless of whether it's herbivorous,
2087 non-eating, or something stranger) */
2088 if (Role_if(PM_MONK) || !u.uconduct.unvegetarian
2089 || !carnivorous(gy.youmonst.data))
2090 Norep("You smell the odor of meat.");
2091 else
2092 Norep("You smell a delicious smell.");
2094 newsym(oox, ooy);
2095 return res;
2099 * Object obj was hit by the effect of the wand/spell otmp. Return
2100 * non-zero if the wand/spell had any effect.
2103 bhito(struct obj *obj, struct obj *otmp)
2105 int res = 1; /* affected object by default */
2106 boolean learn_it = FALSE, maybelearnit;
2108 /* fundamental: a wand effect hitting itself doesn't do anything;
2109 otherwise we need to guard against accessing otmp after something
2110 strange has happened to it (along the lines of polymorph or
2111 stone-to-flesh [which aren't good examples since polymorph wands
2112 aren't affected by polymorph zaps and stone-to-flesh isn't
2113 available in wand form, but the concept still applies...]) */
2114 if (obj == otmp)
2115 return 0;
2117 if (obj->bypass) {
2118 /* The bypass bit is currently only used as follows:
2120 * POLYMORPH - When a monster being polymorphed drops something
2121 * from its inventory as a result of the change.
2122 * If the items fall to the floor, they are not
2123 * subject to direct subsequent polymorphing
2124 * themselves on that same zap. This makes it
2125 * consistent with items that remain in the monster's
2126 * inventory. They are not polymorphed either.
2127 * UNDEAD_TURNING - When an undead creature gets killed via
2128 * undead turning, prevent its corpse from being
2129 * immediately revived by the same effect.
2130 * STONE_TO_FLESH - If a statue can't be revived, its
2131 * contents get dropped before turning it into
2132 * meat; prevent those contents from being hit.
2133 * retouch_equipment() - bypass flag is used to track which
2134 * items have been handled (bhito isn't involved).
2135 * menu_drop(), askchain() - inventory traversal where multiple
2136 * Drop can alter the invent chain while traversal
2137 * is in progress (bhito isn't involved).
2138 * destroy_items() - inventory traversal where item destruction can
2139 * trigger drop or destruction of other item(s) and alter
2140 * the invent or mon->minvent chain, possibly recursively.
2142 * The bypass bit on all objects is reset each turn, whenever
2143 * svc.context.bypasses is set.
2145 * We check the obj->bypass bit above AND svc.context.bypasses
2146 * as a safeguard against any stray occurrence left in an obj
2147 * struct someplace, although that should never happen.
2149 if (svc.context.bypasses) {
2150 return 0;
2151 } else {
2152 debugpline1("%s for a moment.", Tobjnam(obj, "pulsate"));
2153 obj->bypass = 0;
2158 * Some parts of this function expect the object to be on the floor
2159 * obj->{ox,oy} to be valid. The exception to this (so far) is
2160 * for the STONE_TO_FLESH spell.
2162 if (!(obj->where == OBJ_FLOOR || otmp->otyp == SPE_STONE_TO_FLESH))
2163 impossible("bhito: obj is not floor or Stone To Flesh spell");
2165 if (obj == uball) {
2166 res = 0;
2167 } else if (obj == uchain) {
2168 if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) {
2169 learn_it = TRUE;
2170 unpunish();
2171 } else
2172 res = 0;
2173 } else
2174 switch (otmp->otyp) {
2175 case WAN_POLYMORPH:
2176 case SPE_POLYMORPH:
2177 if (obj_unpolyable(obj)) {
2178 res = 0;
2179 break;
2181 /* KMH, conduct */
2182 if (!u.uconduct.polypiles++)
2183 livelog_printf(LL_CONDUCT, "polymorphed %s first object",
2184 uhis());
2186 /* any saved lock context will be dangerously obsolete */
2187 if (Is_box(obj))
2188 (void) boxlock(obj, otmp);
2190 if (obj_shudders(obj)) {
2191 boolean cover = ((obj == svl.level.objects[u.ux][u.uy])
2192 && u.uundetected
2193 && hides_under(gy.youmonst.data));
2195 if (cansee(obj->ox, obj->oy))
2196 learn_it = TRUE;
2197 do_osshock(obj);
2198 /* eek - your cover might have been blown */
2199 if (cover)
2200 (void) hideunder(&gy.youmonst);
2201 break;
2203 obj = poly_obj(obj, STRANGE_OBJECT);
2204 newsym(obj->ox, obj->oy);
2205 break;
2206 case WAN_PROBING:
2207 res = !obj->dknown;
2208 /* target object has now been "seen (up close)" */
2209 obj->dknown = 1;
2210 if (Is_container(obj) || obj->otyp == STATUE) {
2211 obj->cknown = obj->lknown = 1;
2212 /* plural handling here is superfluous because containers
2213 and statues don't stack */
2214 if (obj->otrapped)
2215 pline("%s trapped!", Tobjnam(obj, "are"));
2217 if (!obj->cobj) {
2218 pline("%s empty.", Tobjnam(obj, "are"));
2219 } else if (SchroedingersBox(obj)) {
2220 /* we don't want to force alive vs dead
2221 determination for Schroedinger's Cat here,
2222 so just make probing be inconclusive for it */
2223 You("aren't sure whether %s has %s or its corpse inside.",
2224 the(xname(obj)),
2225 /* unfortunately, we can't tell whether rndmonnam()
2226 picks a form which can't leave a corpse */
2227 an(Hallucination ? rndmonnam((char *) 0) : "cat"));
2228 obj->cknown = 0;
2229 } else {
2230 struct obj *o;
2232 /* view contents (not recursively) */
2233 for (o = obj->cobj; o; o = o->nobj)
2234 o->dknown = 1; /* "seen", even if blind */
2235 (void) display_cinventory(obj);
2237 res = 1;
2238 } else if (obj->otyp == TIN) {
2239 /* don't learn wand if tin is already known */
2240 if (!obj->known || !obj->cknown)
2241 res = 1;
2242 obj->known = 1;
2243 set_cknown_lknown(obj); /* if TIN obj->cknown = 1 */
2244 } else if (obj->otyp == EGG) {
2245 /* if egg is unhatchable, probing it won't learn wand
2246 because even when flagged as known, it's just "an egg" */
2247 if (!obj->known && obj->corpsenm != NON_PM)
2248 res = 1;
2249 obj->known = 1;
2250 /* [should this call learn_egg_type()?] */
2252 if (res)
2253 learn_it = TRUE;
2254 break;
2255 case WAN_STRIKING:
2256 case SPE_FORCE_BOLT:
2257 /* learn the type if you see or hear something break
2258 (the sound could be implicit) */
2259 maybelearnit = cansee(obj->ox, obj->oy) || !Deaf;
2260 if (obj->otyp == BOULDER) {
2261 Soundeffect(se_crumbling_sound, 75);
2262 if (cansee(obj->ox, obj->oy))
2263 pline_The("boulder falls apart.");
2264 else
2265 You_hear("a crumbling sound.");
2266 fracture_rock(obj);
2267 } else if (obj->otyp == STATUE) {
2268 if (break_statue(obj)) {
2269 if (cansee(obj->ox, obj->oy)) {
2270 if (Hallucination)
2271 pline_The("%s shatters.", rndmonnam(NULL));
2272 else
2273 pline_The("statue shatters.");
2274 } else
2275 You_hear("a crumbling sound.");
2277 } else {
2278 int oox = obj->ox, ooy = obj->oy;
2280 if (svc.context.mon_moving ? !breaks(obj, oox, ooy)
2281 : !hero_breaks(obj, oox, ooy, 0))
2282 maybelearnit = FALSE; /* nothing broke */
2283 else
2284 /* obj broke; force redisplay in case it was the only--
2285 or last--item under non-breaking pile-top; top item
2286 here might now be a lone object rather than a pile */
2287 newsym_force(oox, ooy);
2288 res = 0;
2290 if (maybelearnit)
2291 learn_it = TRUE;
2292 break;
2293 case WAN_CANCELLATION:
2294 case SPE_CANCELLATION:
2295 cancel_item(obj);
2296 newsym(obj->ox, obj->oy); /* might change color */
2297 break;
2298 case SPE_DRAIN_LIFE:
2299 (void) drain_item(obj, TRUE);
2300 break;
2301 case WAN_TELEPORTATION:
2302 case SPE_TELEPORT_AWAY:
2304 coordxy ox = obj->ox, oy = obj->oy;
2306 (void) rloco(obj);
2307 maybe_unhide_at(ox, oy);
2309 break;
2310 case WAN_MAKE_INVISIBLE:
2311 break;
2312 case WAN_UNDEAD_TURNING:
2313 case SPE_TURN_UNDEAD:
2314 if (obj->otyp == EGG) {
2315 revive_egg(obj);
2316 } else if (obj->otyp == CORPSE) {
2317 struct monst *mtmp;
2318 coordxy ox, oy;
2319 unsigned save_norevive;
2320 boolean by_u = !svc.context.mon_moving;
2321 int corpsenm = corpse_revive_type(obj);
2322 char *corpsname = cxname_singular(obj);
2324 /* get corpse's location before revive() uses it up */
2325 if (!get_obj_location(obj, &ox, &oy, 0))
2326 ox = obj->ox, oy = obj->oy; /* won't happen */
2328 /* explicit revival magic overrides timer-based no-revive */
2329 save_norevive = obj->norevive;
2330 obj->norevive = 0;
2332 mtmp = revive(obj, TRUE);
2333 if (!mtmp) {
2334 obj->norevive = save_norevive;
2335 res = 0; /* no monster implies corpse was left intact */
2336 } else {
2337 if (cansee(ox, oy)) {
2338 if (canspotmon(mtmp)) {
2339 pline("%s is resurrected!",
2340 upstart(noname_monnam(mtmp, ARTICLE_THE)));
2341 learn_it = by_u ? TRUE : gz.zap_oseen;
2342 } else {
2343 /* saw corpse but don't see monster: maybe
2344 mtmp is invisible, or has been placed at
2345 a different spot than <ox,oy> */
2346 if (!type_is_pname(&mons[corpsenm]))
2347 corpsname = The(corpsname);
2348 pline("%s disappears.", corpsname);
2350 } else {
2351 /* couldn't see corpse's location */
2352 if (Role_if(PM_HEALER) && !Deaf
2353 && !nonliving(&mons[corpsenm])) {
2354 if (!type_is_pname(&mons[corpsenm]))
2355 corpsname = an(corpsname);
2356 if (!Hallucination)
2357 You_hear("%s reviving.", corpsname);
2358 else
2359 You_hear("a defibrillator.");
2360 learn_it = by_u ? TRUE : gz.zap_oseen;
2362 if (canspotmon(mtmp))
2363 /* didn't see corpse but do see monster: it
2364 has been placed somewhere other than <ox,oy>
2365 or blind hero spots it with ESP */
2366 pline("%s appears.", Monnam(mtmp));
2368 if (learn_it)
2369 exercise(A_WIS, TRUE);
2372 break;
2373 case WAN_OPENING:
2374 case SPE_KNOCK:
2375 case WAN_LOCKING:
2376 case SPE_WIZARD_LOCK:
2377 if (Is_box(obj))
2378 res = boxlock(obj, otmp);
2379 else
2380 res = 0;
2381 if (res)
2382 learn_it = TRUE;
2383 break;
2384 case WAN_SLOW_MONSTER: /* no effect on objects */
2385 case SPE_SLOW_MONSTER:
2386 case WAN_SPEED_MONSTER:
2387 case WAN_NOTHING:
2388 case SPE_HEALING:
2389 case SPE_EXTRA_HEALING:
2390 res = 0;
2391 break;
2392 case SPE_STONE_TO_FLESH:
2393 res = stone_to_flesh_obj(obj);
2394 break;
2395 default:
2396 impossible("What an interesting effect (%d)", otmp->otyp);
2397 break;
2399 /* if effect was observable then discover the wand type provided
2400 that the wand itself has been seen */
2401 if (learn_it)
2402 learnwand(otmp);
2403 return res;
2406 /* returns nonzero if something was hit */
2408 bhitpile(
2409 struct obj *obj, /* wand or fake spellbook for type of zap */
2410 int (*fhito)(OBJ_P, OBJ_P), /* callback for each object being hit */
2411 coordxy tx, coordxy ty, /* target location */
2412 schar zz) /* direction for up/down zaps */
2414 struct obj *otmp, *next_obj;
2415 boolean hidingunder, first;
2416 int prevotyp, hitanything = 0;
2418 if (!svl.level.objects[tx][ty])
2419 return 0;
2421 /* if hiding underneath an object and zapping up or down, the top item
2422 is either the only thing hit (up) or is skipped (down) */
2423 hidingunder = (zz != 0 && u.uundetected && hides_under(gy.youmonst.data));
2424 first = TRUE;
2426 if (obj->otyp == SPE_FORCE_BOLT || obj->otyp == WAN_STRIKING) {
2427 struct trap *t = t_at(tx, ty);
2428 struct obj *topofpile = svl.level.objects[tx][ty];
2430 /* We can't settle for the default calling sequence of
2431 bhito(otmp) -> break_statue(otmp) -> activate_statue_trap(ox,oy)
2432 because that last call might end up operating on our `next_obj'
2433 (below), rather than on the current object, if it happens to
2434 encounter a statue which mustn't become animated. */
2435 if (t && t->ttyp == STATUE_TRAP
2436 && activate_statue_trap(t, tx, ty, TRUE))
2437 learnwand(obj);
2438 /* assume zapping up or down while hiding under the top item can
2439 still activate the trap even if it's below (when zapping up)
2440 or above (when zapping down) */
2441 if (svl.level.objects[tx][ty] != topofpile)
2442 first = FALSE; /* top item was statue which activated */
2445 gp.poly_zapped = -1;
2446 for (otmp = svl.level.objects[tx][ty]; otmp; otmp = next_obj) {
2447 next_obj = otmp->nexthere;
2448 if (hidingunder) {
2449 if (first) {
2450 first = FALSE; /* reset for next item */
2451 if (zz > 0) /* down when hiding-under skips first item */
2452 continue;
2453 } else {
2454 /* !first */
2455 if (zz < 0) /* up when hiding-under skips rest of pile */
2456 continue;
2459 if (otmp->where != OBJ_FLOOR || otmp->ox != tx || otmp->oy != ty)
2460 continue;
2461 hitanything += (*fhito)(otmp, obj);
2464 if (gp.poly_zapped >= 0)
2465 create_polymon(svl.level.objects[tx][ty], gp.poly_zapped);
2467 /* when boulders are present they're expected to be on top; with
2468 multiple boulders it's possible for some to have been changed into
2469 non-boulders (polymorph, stone-to-flesh) while ones beneath resist,
2470 so re-stack pile if there are any non-boulders above boulders */
2471 prevotyp = BOULDER;
2472 for (otmp = svl.level.objects[tx][ty]; otmp; otmp = otmp->nexthere) {
2473 if (otmp->otyp == BOULDER && prevotyp != BOULDER) {
2474 recreate_pile_at(tx, ty);
2475 break;
2477 prevotyp = otmp->otyp;
2480 if (hidingunder) /* pile might have been destroyed or dispersed */
2481 maybe_unhide_at(tx, ty);
2483 fill_pit(tx, ty);
2485 return hitanything;
2489 * zappable - returns 1 if zap is available, 0 otherwise.
2490 * it removes a charge from the wand if zappable.
2491 * added by GAN 11/03/86
2494 zappable(struct obj *wand)
2496 if (wand->spe < 0 || (wand->spe == 0 && rn2(WAND_WREST_CHANCE)))
2497 return 0;
2498 if (wand->spe == 0)
2499 You("wrest one last charge from the worn-out wand.");
2500 wand->spe--;
2501 return 1;
2504 void
2505 do_enlightenment_effect(void)
2507 You_feel("self-knowledgeable...");
2508 display_nhwindow(WIN_MESSAGE, FALSE);
2509 enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS);
2510 pline_The("feeling subsides.");
2511 exercise(A_WIS, TRUE);
2515 * zapnodir - zaps a NODIR wand/spell.
2516 * Won't get here if wand has no charges (unless wresting 1 last charge).
2518 void
2519 zapnodir(struct obj *obj)
2521 boolean known = FALSE;
2523 switch (obj->otyp) {
2524 case WAN_LIGHT:
2525 case SPE_LIGHT:
2526 /* FIXME? wand of light becoming discovered should be contingent upon
2527 seeing at least one previously unlit spot become lit */
2528 known = (obj->dknown && !Blind);
2529 litroom(TRUE, obj);
2530 (void) lightdamage(obj, TRUE, 5);
2531 break;
2532 case WAN_SECRET_DOOR_DETECTION:
2533 case SPE_DETECT_UNSEEN:
2534 /* findit() gives sufficient feedback to discover the wand even when
2535 blinded or when it fails to find anything */
2536 known = !!obj->dknown;
2537 (void) findit();
2538 break;
2539 case WAN_CREATE_MONSTER:
2540 /* create_critters() returns True iff hero sees a new monster appear */
2541 if (create_critters(rn2(23) ? 1 : rn1(7, 2),
2542 (struct permonst *) 0, FALSE))
2543 known = !!obj->dknown;
2544 break;
2545 case WAN_WISHING:
2546 if (Luck + rn2(5) < 0) {
2547 pline("Unfortunately, nothing happens.");
2548 known = FALSE;
2549 } else {
2550 known = !!obj->dknown;
2551 /* wand of wishing asks player what to wish for so always becomes
2552 discovered (unless it hasn't been seen) */
2553 makewish();
2555 break;
2556 case WAN_ENLIGHTENMENT:
2557 known = !!obj->dknown;
2558 /* do_enlightenmnt_effect() always describes enlightenment */
2559 do_enlightenment_effect();
2560 break;
2561 default:
2562 break;
2565 if (known) {
2566 if (!objects[obj->otyp].oc_name_known)
2567 more_experienced(0, 10);
2568 /* effect was observable; discover the wand type provided
2569 that the wand itself has been seen */
2570 learnwand(obj);
2574 staticfn void
2575 backfire(struct obj *otmp)
2577 int dmg;
2579 otmp->in_use = TRUE; /* in case losehp() is fatal */
2580 pline("%s suddenly explodes!", The(xname(otmp)));
2581 dmg = d(otmp->spe + 2, 6);
2582 losehp(Maybe_Half_Phys(dmg), "exploding wand", KILLED_BY_AN);
2583 useupall(otmp);
2586 /* getobj callback for object to zap */
2587 staticfn int
2588 zap_ok(struct obj *obj)
2590 if (obj && obj->oclass == WAND_CLASS)
2591 return GETOBJ_SUGGEST;
2592 return GETOBJ_EXCLUDE;
2595 /* #zap command, 'z' (or 'y' if numbed_pad==-1) */
2597 dozap(void)
2599 struct obj *obj;
2600 int damage, need_dir;
2602 if (nohands(gy.youmonst.data)) {
2603 You("aren't able to zap anything in your current form.");
2604 return ECMD_OK;
2606 if (check_capacity((char *) 0))
2607 return ECMD_OK;
2608 obj = getobj("zap", zap_ok, GETOBJ_NOFLAGS);
2609 if (!obj)
2610 return ECMD_CANCEL;
2612 check_unpaid(obj);
2614 need_dir = objects[obj->otyp].oc_dir != NODIR;
2615 if (!zappable(obj)) {
2616 pline1(nothing_happens);
2617 } else if (obj->cursed && !rn2(WAND_BACKFIRE_CHANCE)) {
2618 backfire(obj); /* the wand blows up in your face! */
2619 exercise(A_STR, FALSE);
2620 /* 'obj' is gone; skip update_inventory() because
2621 backfire() -> useupall() -> freeinv() did it */
2622 return ECMD_TIME;
2623 } else if (need_dir && !getdir((char *) 0)) {
2624 if (!Blind)
2625 pline("%s glows and fades.", The(xname(obj)));
2626 /* make him pay for knowing !NODIR */
2627 } else if (need_dir && !u.dx && !u.dy && !u.dz) {
2628 if ((damage = zapyourself(obj, TRUE)) != 0) {
2629 char buf[BUFSZ];
2631 Sprintf(buf, "zapped %sself with %s",
2632 uhim(), killer_xname(obj));
2633 losehp(Maybe_Half_Phys(damage), buf, NO_KILLER_PREFIX);
2635 } else {
2636 /* Are we having fun yet?
2637 * weffects -> buzz(obj->otyp) -> zhitm (temple priest) ->
2638 * attack -> hitum -> known_hitum -> ghod_hitsu ->
2639 * buzz(AD_ELEC) -> destroy_items(AD_ELEC) ->
2640 * useup -> obfree -> dealloc_obj -> free(obj)
2642 gc.current_wand = obj;
2643 weffects(obj);
2644 obj = gc.current_wand;
2645 gc.current_wand = 0;
2647 if (obj && obj->spe < 0) {
2648 pline("%s to dust.", Tobjnam(obj, "turn"));
2649 useupall(obj); /* calls freeinv() -> update_inventory() */
2650 } else
2651 update_inventory(); /* maybe used a charge */
2652 return ECMD_TIME;
2655 /* Lock or unlock all boxes in inventory */
2656 staticfn void
2657 boxlock_invent(struct obj *obj)
2659 struct obj *otmp, *nextobj;
2660 boolean boxing = FALSE;
2662 /* (un)lock carried boxes */
2663 for (otmp = gi.invent; otmp; otmp = nextobj) {
2664 nextobj = otmp->nobj;
2665 if (Is_box(otmp)) {
2666 (void) boxlock(otmp, obj);
2667 boxing = TRUE;
2670 if (boxing)
2671 update_inventory(); /* in case any box->lknown has changed */
2675 zapyourself(struct obj *obj, boolean ordinary)
2677 boolean learn_it = FALSE;
2678 int damage = 0;
2679 int orig_dmg = 0; /* for passing to destroy_items() */
2681 switch (obj->otyp) {
2682 case WAN_STRIKING:
2683 case SPE_FORCE_BOLT:
2684 learn_it = TRUE;
2685 if (Antimagic) {
2686 shieldeff(u.ux, u.uy);
2687 pline("Boing!");
2688 monstseesu(M_SEEN_MAGR);
2689 } else {
2690 if (ordinary) {
2691 You("bash yourself!");
2692 damage = d(2, 12);
2693 } else
2694 damage = d(1 + obj->spe, 6);
2695 exercise(A_STR, FALSE);
2696 monstunseesu(M_SEEN_MAGR);
2698 break;
2700 case WAN_LIGHTNING:
2701 learn_it = TRUE;
2702 orig_dmg = d(12, 6);
2703 if (!Shock_resistance) {
2704 You("shock yourself!");
2705 damage = orig_dmg;
2706 exercise(A_CON, FALSE);
2707 monstunseesu(M_SEEN_ELEC);
2708 } else {
2709 shieldeff(u.ux, u.uy);
2710 You("zap yourself, but seem unharmed.");
2711 monstseesu(M_SEEN_ELEC);
2712 ugolemeffects(AD_ELEC, orig_dmg);
2714 (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dmg);
2715 (void) flashburn((long) rnd(100), TRUE);
2716 break;
2718 case SPE_FIREBALL:
2719 You("explode a fireball on top of yourself!");
2720 explode(u.ux, u.uy, 11, d(6, 6), WAND_CLASS, EXPL_FIERY);
2721 break;
2722 case WAN_FIRE:
2723 case FIRE_HORN:
2724 learn_it = TRUE;
2725 orig_dmg = d(12, 6);
2726 if (Fire_resistance) {
2727 shieldeff(u.ux, u.uy);
2728 You_feel("rather warm.");
2729 monstseesu(M_SEEN_FIRE);
2730 ugolemeffects(AD_FIRE, orig_dmg);
2731 } else {
2732 pline("You've set yourself afire!");
2733 damage = orig_dmg;
2734 monstunseesu(M_SEEN_FIRE);
2736 burn_away_slime();
2737 (void) burnarmor(&gy.youmonst);
2738 (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dmg);
2739 ignite_items(gi.invent);
2740 break;
2742 case WAN_COLD:
2743 case SPE_CONE_OF_COLD:
2744 case FROST_HORN:
2745 learn_it = TRUE;
2746 orig_dmg = d(12, 6);
2747 if (Cold_resistance) {
2748 shieldeff(u.ux, u.uy);
2749 You_feel("a little chill.");
2750 monstseesu(M_SEEN_COLD);
2751 ugolemeffects(AD_COLD, orig_dmg);
2752 } else {
2753 You("imitate a popsicle!");
2754 damage = orig_dmg;
2755 monstunseesu(M_SEEN_COLD);
2757 (void) destroy_items(&gy.youmonst, AD_COLD, orig_dmg);
2758 break;
2760 case WAN_MAGIC_MISSILE:
2761 case SPE_MAGIC_MISSILE:
2762 learn_it = TRUE;
2763 if (Antimagic) {
2764 shieldeff(u.ux, u.uy);
2765 pline_The("missiles bounce!");
2766 monstseesu(M_SEEN_MAGR);
2767 } else {
2768 damage = d(4, 6);
2769 pline("Idiot! You've shot yourself!");
2770 monstunseesu(M_SEEN_MAGR);
2772 break;
2774 case WAN_POLYMORPH:
2775 case SPE_POLYMORPH:
2776 if (!Unchanging) {
2777 learn_it = TRUE;
2778 polyself(POLY_NOFLAGS);
2780 break;
2782 case WAN_CANCELLATION:
2783 case SPE_CANCELLATION:
2784 (void) cancel_monst(&gy.youmonst, obj, TRUE, TRUE, TRUE);
2785 break;
2787 case SPE_DRAIN_LIFE:
2788 if (!Drain_resistance) {
2789 learn_it = TRUE; /* (no effect for spells...) */
2790 losexp("life drainage");
2792 damage = 0; /* No additional damage */
2793 break;
2795 case WAN_MAKE_INVISIBLE: {
2796 /* have to test before changing HInvis but must change
2797 * HInvis before doing newsym().
2799 int msg = !Invis && !Blind && !BInvis;
2801 if (BInvis && uarmc->otyp == MUMMY_WRAPPING) {
2802 /* A mummy wrapping absorbs it and protects you */
2803 You_feel("rather itchy under %s.", yname(uarmc));
2804 break;
2806 incr_itimeout(&HInvis, rn1(15, 31));
2807 if (msg) {
2808 learn_it = TRUE;
2809 newsym(u.ux, u.uy);
2810 self_invis_message();
2812 break;
2815 case WAN_SPEED_MONSTER:
2816 /* no longer gives intrinsic, but gives very fast speed instead */
2817 speed_up(rn1(25, 50));
2818 learn_it = TRUE;
2819 break;
2821 case WAN_SLEEP:
2822 case SPE_SLEEP:
2823 learn_it = TRUE;
2824 if (Sleep_resistance) {
2825 shieldeff(u.ux, u.uy);
2826 You("don't feel sleepy!");
2827 monstseesu(M_SEEN_SLEEP);
2828 } else {
2829 if (ordinary)
2830 pline_The("sleep ray hits you!");
2831 else
2832 You("fall asleep!");
2833 monstunseesu(M_SEEN_SLEEP);
2834 fall_asleep(-rnd(50), TRUE);
2836 break;
2838 case WAN_SLOW_MONSTER:
2839 case SPE_SLOW_MONSTER:
2840 if (HFast & (TIMEOUT | INTRINSIC)) {
2841 learn_it = TRUE;
2842 u_slow_down();
2844 break;
2846 case WAN_TELEPORTATION:
2847 case SPE_TELEPORT_AWAY:
2848 tele();
2849 /* same criteria as when mounted (zap_steed) */
2850 if ((Teleport_control && !Stunned) || !couldsee(u.ux0, u.uy0)
2851 || distu(u.ux0, u.uy0) >= 16)
2852 learn_it = TRUE;
2853 break;
2855 case WAN_DEATH:
2856 case SPE_FINGER_OF_DEATH:
2857 if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) {
2858 pline((obj->otyp == WAN_DEATH)
2859 ? "The wand shoots an apparently harmless beam at you."
2860 : "You seem no deader than before.");
2861 break;
2863 learn_it = TRUE;
2864 Sprintf(svk.killer.name, "shot %sself with a death ray", uhim());
2865 svk.killer.format = NO_KILLER_PREFIX;
2866 /* probably don't need these to be urgent; player just gave input
2867 without subsequent opportunity to dismiss --More-- with ESC */
2868 urgent_pline("You irradiate yourself with pure energy!");
2869 urgent_pline("You die.");
2870 /* They might survive with an amulet of life saving */
2871 done(DIED);
2872 break;
2873 case WAN_UNDEAD_TURNING:
2874 case SPE_TURN_UNDEAD:
2875 learn_it = TRUE;
2876 unturn_you();
2877 break;
2878 case SPE_HEALING:
2879 case SPE_EXTRA_HEALING:
2880 learn_it = TRUE; /* (no effect for spells...) */
2881 healup(d(6, obj->otyp == SPE_EXTRA_HEALING ? 8 : 4), 0, FALSE,
2882 (obj->blessed || obj->otyp == SPE_EXTRA_HEALING));
2883 You_feel("%sbetter.", obj->otyp == SPE_EXTRA_HEALING ? "much " : "");
2884 break;
2885 case WAN_LIGHT: /* (broken wand) */
2886 /* assert( !ordinary ); */
2887 damage = d(obj->spe, 25);
2888 FALLTHROUGH;
2889 /*FALLTHRU*/
2890 case EXPENSIVE_CAMERA:
2891 if (!damage)
2892 damage = 5;
2893 damage = lightdamage(obj, ordinary, damage);
2894 damage += rnd(25);
2895 if (flashburn((long) damage, FALSE))
2896 learn_it = TRUE;
2897 damage = 0; /* reset */
2898 break;
2899 case WAN_OPENING:
2900 case SPE_KNOCK:
2901 if (u.ustuck) {
2902 /* zapping either self or holder/holdee [bhitm()] will release
2903 holder's grasp from the hero or hero's grasp from holdee */
2904 release_hold();
2905 learn_it = TRUE;
2907 if (Punished) {
2908 learn_it = TRUE;
2909 unpunish();
2911 /* invent is hit iff hero doesn't escape from a trap */
2912 if (!u.utrap || !openholdingtrap(&gy.youmonst, &learn_it)) {
2913 boxlock_invent(obj);
2914 /* trigger previously escaped trapdoor */
2915 (void) openfallingtrap(&gy.youmonst, TRUE, &learn_it);
2917 break;
2918 case WAN_LOCKING:
2919 case SPE_WIZARD_LOCK:
2920 /* similar logic to opening; invent is hit iff no trap triggered */
2921 if (u.utrap || !closeholdingtrap(&gy.youmonst, &learn_it)) {
2922 boxlock_invent(obj);
2924 break;
2925 case WAN_DIGGING:
2926 case SPE_DIG:
2927 case SPE_DETECT_UNSEEN:
2928 case WAN_NOTHING:
2929 break;
2930 case WAN_PROBING:
2931 probe_objchain(gi.invent);
2932 update_inventory();
2933 learn_it = TRUE;
2934 ustatusline();
2935 break;
2936 case SPE_STONE_TO_FLESH: {
2937 struct obj *otmp, *onxt;
2938 boolean didmerge;
2940 if (u.umonnum == PM_STONE_GOLEM) {
2941 learn_it = TRUE;
2942 (void) polymon(PM_FLESH_GOLEM);
2944 if (Stoned) {
2945 learn_it = TRUE;
2946 fix_petrification(); /* saved! */
2948 /* but at a cost.. */
2949 for (otmp = gi.invent; otmp; otmp = onxt) {
2950 onxt = otmp->nobj;
2951 if (bhito(otmp, obj))
2952 learn_it = TRUE;
2955 * It is possible that we can now merge some inventory.
2956 * Do a highly paranoid merge. Restart from the beginning until
2957 * no merges. Don't merge worn items (in case of stone-to-flesh
2958 * of rocks wielded in differing weapon/alt-wep/quiver slot).
2960 do {
2961 didmerge = FALSE;
2962 for (otmp = gi.invent; !didmerge && otmp; otmp = otmp->nobj) {
2963 if (otmp->owornmask)
2964 continue;
2965 for (onxt = otmp->nobj; onxt; onxt = onxt->nobj)
2966 if (merged(&otmp, &onxt)) {
2967 didmerge = TRUE;
2968 break;
2971 } while (didmerge);
2972 break;
2974 default:
2975 impossible("zapyourself: object %d used?", obj->otyp);
2976 break;
2978 /* if effect was observable then discover the wand type provided
2979 that the wand itself has been seen */
2980 if (learn_it)
2981 learnwand(obj);
2982 return damage;
2985 /* called when poly'd hero uses breath attack against self */
2986 void
2987 ubreatheu(struct attack *mattk)
2989 int dtyp = 20 + mattk->adtyp - 1; /* breath by hero */
2991 zhitu(dtyp, mattk->damn, flash_str(dtyp, TRUE), u.ux, u.uy);
2994 /* light damages hero in gremlin form */
2996 lightdamage(
2997 struct obj *obj, /* item making light (fake book if spell) */
2998 boolean ordinary, /* wand/camera zap vs wand destruction */
2999 int amt) /* pseudo-damage used to determine blindness duration */
3001 char buf[BUFSZ];
3002 const char *how;
3003 int dmg = amt;
3005 if (dmg && gy.youmonst.data == &mons[PM_GREMLIN]) {
3006 /* reduce high values (from destruction of wand with many charges) */
3007 dmg = rnd(dmg);
3008 if (dmg > 10)
3009 dmg = 10 + rnd(dmg - 10);
3010 if (dmg > 20)
3011 dmg = 20;
3012 pline("Ow, that light hurts%c", (dmg > 2 || u.mh <= 5) ? '!' : '.');
3013 /* [composing killer/reason is superfluous here; if fatal, cause
3014 of death will always be "killed while stuck in creature form"] */
3015 if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS)
3016 ordinary = FALSE; /* say blasted rather than zapped */
3017 how = (obj->oclass == SPBOOK_CLASS) ? "spell of light"
3018 : (!obj->oartifact) ? ansimpleoname(obj)
3019 : bare_artifactname(obj);
3020 Sprintf(buf, "%s %sself with %s", ordinary ? "zapped" : "blasted",
3021 uhim(), how);
3022 /* might rehumanize(); could be fatal, but only for Unchanging */
3023 losehp(Maybe_Half_Phys(dmg), buf, NO_KILLER_PREFIX);
3025 return dmg;
3028 /* light[ning] causes blindness */
3029 boolean
3030 flashburn(long duration, boolean via_lightning)
3032 if (!resists_blnd(&gy.youmonst)) {
3033 You(are_blinded_by_the_flash);
3034 make_blinded(duration, FALSE);
3035 if (!Blind)
3036 Your1(vision_clears);
3037 return TRUE;
3039 /* if blinding is resisted due to magical equipment (Sunsword), give
3040 a sparkle animation (even if also resisted due to being blind)
3041 _unless_ this is lightning-induced; we don't want a double sparkle
3042 if hero is both lightning resistant and blindness resistant, or
3043 worse, have a single sparkle where the player confuses blindness
3044 resistance for lightning resistance */
3045 if (!via_lightning && resists_blnd_by_arti(&gy.youmonst)) {
3046 shieldeff(u.ux, u.uy);
3047 return TRUE;
3049 return FALSE;
3052 /* you've zapped a wand downwards while riding
3053 * Return TRUE if the steed was hit by the wand.
3054 * Return FALSE if the steed was not hit by the wand.
3056 staticfn boolean
3057 zap_steed(struct obj *obj) /* wand or spell */
3059 int steedhit = FALSE;
3061 gb.bhitpos.x = u.usteed->mx, gb.bhitpos.y = u.usteed->my;
3062 gn.notonhead = FALSE;
3063 switch (obj->otyp) {
3065 * Wands that are allowed to hit the steed
3066 * Carefully test the results of any that are
3067 * moved here from the bottom section.
3069 case WAN_PROBING:
3070 probe_monster(u.usteed);
3071 learnwand(obj);
3072 steedhit = TRUE;
3073 break;
3074 case WAN_TELEPORTATION:
3075 case SPE_TELEPORT_AWAY:
3076 /* you go together */
3077 tele();
3078 /* same criteria as when unmounted (zapyourself) */
3079 if ((Teleport_control && !Stunned) || !couldsee(u.ux0, u.uy0)
3080 || distu(u.ux0, u.uy0) >= 16)
3081 learnwand(obj);
3082 steedhit = TRUE;
3083 break;
3085 /* Default processing via bhitm() for these */
3086 case SPE_CURE_SICKNESS:
3087 case WAN_MAKE_INVISIBLE:
3088 case WAN_CANCELLATION:
3089 case SPE_CANCELLATION:
3090 case WAN_POLYMORPH:
3091 case SPE_POLYMORPH:
3092 case WAN_STRIKING:
3093 case SPE_FORCE_BOLT:
3094 case WAN_SLOW_MONSTER:
3095 case SPE_SLOW_MONSTER:
3096 case WAN_SPEED_MONSTER:
3097 case SPE_HEALING:
3098 case SPE_EXTRA_HEALING:
3099 case SPE_DRAIN_LIFE:
3100 case WAN_OPENING:
3101 case SPE_KNOCK:
3102 (void) bhitm(u.usteed, obj);
3103 steedhit = TRUE;
3104 break;
3106 default:
3107 steedhit = FALSE;
3108 break;
3110 return steedhit;
3114 * cancel a monster (possibly the hero). inventory is cancelled only
3115 * if the monster is zapping itself directly, since otherwise the
3116 * effect is too strong. currently non-hero monsters do not zap
3117 * themselves with cancellation.
3119 boolean
3120 cancel_monst(struct monst *mdef, struct obj *obj, boolean youattack,
3121 boolean allow_cancel_kill, boolean self_cancel)
3123 static const char
3124 writing_vanishes[] = "Some writing vanishes from %s head!",
3125 your[] = "your"; /* should be extern */
3126 boolean youdefend = (mdef == &gy.youmonst);
3128 if (youdefend ? (!youattack && Antimagic)
3129 : resist(mdef, obj->oclass, 0, NOTELL))
3130 return FALSE; /* resisted cancellation */
3132 if (self_cancel) { /* 1st cancel inventory */
3133 struct obj *otmp;
3135 for (otmp = (youdefend ? gi.invent : mdef->minvent); otmp;
3136 otmp = otmp->nobj)
3137 cancel_item(otmp);
3139 if (youdefend) {
3140 disp.botl = TRUE; /* potential AC change */
3141 find_ac();
3142 /* update_inventory(); -- handled by caller */
3146 /* now handle special cases */
3147 if (youdefend) {
3148 if (Upolyd) { /* includes lycanthrope in creature form */
3150 * Return to normal form unless Unchanging.
3151 * Hero in clay golem form dies if Unchanging.
3152 * Does not cure lycanthropy or stop timed random polymorph.
3154 if (u.umonnum == PM_CLAY_GOLEM) {
3155 if (!Blind)
3156 pline(writing_vanishes, your);
3157 else /* note: "dark" rather than "heavy" is intentional... */
3158 You_feel("%s headed.", Hallucination ? "dark" : "light");
3159 u.mh = 0; /* fatal; death handled by rehumanize() */
3161 if (Unchanging && u.mh > 0)
3162 Your("amulet grows hot for a moment, then cools.");
3163 else
3164 rehumanize();
3166 } else {
3167 mdef->mcan = 1;
3168 /* force shapeshifter into its base form or mimic to unhide */
3169 normal_shape(mdef);
3171 if (mdef->data == &mons[PM_CLAY_GOLEM]) {
3172 if (canseemon(mdef))
3173 pline(writing_vanishes, s_suffix(mon_nam(mdef)));
3174 /* !allow_cancel_kill is for Magicbane, where clay golem
3175 will be killed somewhere back up the call/return chain... */
3176 if (allow_cancel_kill) {
3177 if (youattack)
3178 killed(mdef);
3179 else
3180 monkilled(mdef, "", AD_SPEL);
3184 return TRUE;
3187 /* you've zapped an immediate type wand up or down */
3188 staticfn boolean
3189 zap_updown(struct obj *obj) /* wand or spell, nonnull */
3191 boolean striking = FALSE, disclose = FALSE, map_zapped = FALSE;
3192 coordxy x, y, xx, yy;
3193 int ptmp;
3194 struct obj *otmp;
3195 struct engr *e;
3196 struct trap *ttmp;
3197 stairway *stway = gs.stairs;
3199 /* some wands have special effects other than normal bhitpile */
3200 /* drawbridge might change <u.ux,u.uy> */
3201 x = xx = u.ux; /* <x,y> is zap location */
3202 y = yy = u.uy; /* <xx,yy> is drawbridge (portcullis) position */
3203 ttmp = t_at(x, y); /* trap if there is one */
3205 switch (obj->otyp) {
3206 case WAN_PROBING:
3207 ptmp = 0;
3208 if (u.dz < 0) {
3209 You("probe towards the %s.", ceiling(x, y));
3210 } else { /* down */
3211 const char *surf;
3212 schar ltyp, rememberedltyp = update_mapseen_for(x, y);
3214 ptmp += bhitpile(obj, bhito, x, y, u.dz);
3215 /* sequencing: zap_map() calls force_decor() for ice or furniture;
3216 we need to call it before probing for buried objects */
3217 ltyp = SURFACE_AT(x, y);
3218 zap_map(x, y, obj);
3219 /*map_zapped = TRUE; // not needed due to early return*/
3220 if (ltyp == ICE || IS_FURNITURE(ltyp)) {
3221 surf = "it";
3222 if (svl.lastseentyp[x][y] != rememberedltyp)
3223 ptmp += 1;
3224 } else {
3225 surf = the(surface(x, y));
3227 You("probe beneath %s.", surf);
3228 ptmp += display_binventory(x, y, TRUE);
3230 if (!ptmp)
3231 Your("probe reveals nothing.");
3232 return TRUE; /* we've done our own bhitpile */
3233 case WAN_OPENING:
3234 case SPE_KNOCK:
3235 while (stway) {
3236 if (!stway->isladder && !stway->up
3237 && stway->tolev.dnum == u.uz.dnum)
3238 break;
3239 stway = stway->next;
3241 /* up or down, but at closed portcullis only */
3242 if (is_db_wall(x, y) && find_drawbridge(&xx, &yy)) {
3243 open_drawbridge(xx, yy);
3244 disclose = TRUE;
3245 } else if (u.dz > 0 && stway && stway->sx == x && stway->sy == y
3246 /* can't use the stairs down to quest level 2 until
3247 leader "unlocks" them; give feedback if you try */
3248 && on_level(&u.uz, &qstart_level) && !ok_to_quest()) {
3249 pline_The("stairs seem to ripple momentarily.");
3250 disclose = TRUE;
3252 /* down will release you from bear trap or web */
3253 if (u.dz > 0 && u.utrap) {
3254 (void) openholdingtrap(&gy.youmonst, &disclose);
3255 /* down will trigger trapdoor, hole, or [spiked-] pit */
3256 } else if (u.dz > 0 && !u.utrap) {
3257 (void) openfallingtrap(&gy.youmonst, FALSE, &disclose);
3259 break;
3260 case WAN_STRIKING:
3261 case SPE_FORCE_BOLT:
3262 striking = TRUE;
3263 FALLTHROUGH;
3264 /*FALLTHRU*/
3265 case WAN_LOCKING:
3266 case SPE_WIZARD_LOCK:
3267 /* down at open bridge or up or down at open portcullis */
3268 if (((levl[x][y].typ == DRAWBRIDGE_DOWN)
3269 ? (u.dz > 0)
3270 : (is_drawbridge_wall(x, y) >= 0 && !is_db_wall(x, y)))
3271 && find_drawbridge(&xx, &yy)) {
3272 if (!striking)
3273 close_drawbridge(xx, yy);
3274 else
3275 destroy_drawbridge(xx, yy);
3276 disclose = TRUE;
3277 } else if (striking && u.dz < 0 && rn2(3) && !Is_airlevel(&u.uz)
3278 && !Is_waterlevel(&u.uz) && !Underwater
3279 && !Is_qstart(&u.uz)) {
3280 int dmg;
3281 /* similar to zap_dig() */
3282 pline("A rock is dislodged from the %s and falls on your %s.",
3283 ceiling(x, y), body_part(HEAD));
3284 dmg = rnd(hard_helmet(uarmh) ? 2 : 6);
3285 losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN);
3286 if ((otmp = mksobj_at(ROCK, x, y, FALSE, FALSE)) != 0) {
3287 (void) xname(otmp); /* set dknown, maybe bknown */
3288 stackobj(otmp);
3290 newsym(x, y);
3291 } else if (u.dz > 0 && ttmp) {
3292 if (!striking && closeholdingtrap(&gy.youmonst, &disclose)) {
3293 ; /* now stuck in web or bear trap */
3294 } else if (striking && ttmp->ttyp == TRAPDOOR) {
3295 /* striking transforms trapdoor into hole */
3296 if (Blind && !ttmp->tseen) {
3297 pline("%s beneath you shatters.", Something);
3298 } else if (!ttmp->tseen) { /* => !Blind */
3299 pline("There's a trapdoor beneath you; it shatters.");
3300 } else {
3301 pline("The trapdoor beneath you shatters.");
3302 disclose = TRUE;
3304 ttmp->ttyp = HOLE;
3305 ttmp->tseen = 1;
3306 newsym(x, y);
3307 /* might fall down hole */
3308 dotrap(ttmp, NO_TRAP_FLAGS);
3309 } else if (!striking && ttmp->ttyp == HOLE) {
3310 /* locking transforms hole into trapdoor */
3311 ttmp->ttyp = TRAPDOOR;
3312 if (Blind || !ttmp->tseen) {
3313 pline("Some %s swirls beneath you.",
3314 is_ice(x, y) ? "frost" : "dust");
3315 } else {
3316 ttmp->tseen = 1;
3317 newsym(x, y);
3318 pline("A trapdoor appears beneath you.");
3319 disclose = TRUE;
3321 /* hadn't fallen down hole; won't fall now */
3324 break;
3325 case SPE_STONE_TO_FLESH:
3326 if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) || Underwater
3327 || (Is_qstart(&u.uz) && u.dz < 0)) {
3328 pline1(nothing_happens);
3329 } else if (u.dz < 0) { /* we should do more... */
3330 pline("Blood drips on your %s.", body_part(FACE));
3331 } else if (u.dz > 0 && !OBJ_AT(u.ux, u.uy)) {
3333 Print this message only if there wasn't an engraving
3334 affected here. If water or ice, act like waterlevel case.
3336 e = engr_at(u.ux, u.uy);
3337 if (!(e && e->engr_type == ENGRAVE)) {
3338 if (is_pool(u.ux, u.uy) || is_ice(u.ux, u.uy))
3339 pline1(nothing_happens);
3340 else
3341 pline("Blood %ss %s your %s.",
3342 is_lava(u.ux, u.uy) ? "boil" : "pool",
3343 Levitation ? "beneath" : "at",
3344 makeplural(body_part(FOOT)));
3347 break;
3348 default:
3349 break;
3352 if (u.dz > 0) {
3353 /* zapping downward */
3354 (void) bhitpile(obj, bhito, x, y, u.dz);
3356 /* note: engraving handling that used to be here has been moved
3357 to zap_map() */
3358 if (!map_zapped)
3359 zap_map(x, y, obj);
3361 } else if (u.dz < 0) {
3362 /* zapping upward */
3364 /* game flavor: if you're hiding under "something"
3365 * a zap upward should hit that "something".
3367 if (u.uundetected && hides_under(gy.youmonst.data)) {
3368 int hitit = 0;
3369 otmp = svl.level.objects[u.ux][u.uy];
3371 if (otmp)
3372 hitit = bhito(otmp, obj);
3373 if (hitit) {
3374 (void) hideunder(&gy.youmonst);
3375 disclose = TRUE;
3380 return disclose;
3383 /* used by do_break_wand() was well as by weffects() */
3384 void
3385 zapsetup(void)
3387 go.obj_zapped = FALSE;
3390 void
3391 zapwrapup(void)
3393 /* if do_osshock() set obj_zapped while polying, give a message now */
3394 if (go.obj_zapped)
3395 You_feel("shuddering vibrations.");
3396 go.obj_zapped = FALSE;
3399 /* called for various wand and spell effects - M. Stephenson */
3400 void
3401 weffects(struct obj *obj)
3403 int otyp = obj->otyp;
3404 boolean disclose = FALSE, was_unkn = !objects[otyp].oc_name_known;
3406 exercise(A_WIS, TRUE);
3407 if (u.usteed && (objects[otyp].oc_dir != NODIR) && !u.dx && !u.dy
3408 && (u.dz > 0) && zap_steed(obj)) {
3409 disclose = TRUE;
3410 } else if (objects[otyp].oc_dir == IMMEDIATE) {
3411 zapsetup(); /* reset obj_zapped */
3412 if (u.uswallow) {
3413 (void) bhitm(u.ustuck, obj);
3414 /* [how about `bhitpile(u.ustuck->minvent)' effect?] */
3415 } else if (u.dz) {
3416 disclose = zap_updown(obj);
3417 } else {
3418 (void) bhit(u.dx, u.dy, rn1(8, 6), ZAPPED_WAND, bhitm, bhito,
3419 &obj);
3421 zapwrapup(); /* give feedback for obj_zapped */
3423 } else if (objects[otyp].oc_dir == NODIR) {
3424 zapnodir(obj);
3426 } else {
3427 /* neither immediate nor directionless */
3429 if (otyp == WAN_DIGGING || otyp == SPE_DIG)
3430 zap_dig();
3431 else if (otyp >= SPE_MAGIC_MISSILE && otyp <= SPE_FINGER_OF_DEATH)
3432 ubuzz(BZ_U_SPELL(BZ_OFS_SPE(otyp)), u.ulevel / 2 + 1);
3433 else if (otyp >= WAN_MAGIC_MISSILE && otyp <= WAN_LIGHTNING)
3434 ubuzz(BZ_U_WAND(BZ_OFS_WAN(otyp)),
3435 (otyp == WAN_MAGIC_MISSILE) ? 2 : 6);
3436 else
3437 impossible("weffects: unexpected spell or wand");
3438 disclose = TRUE;
3440 if (disclose) {
3441 learnwand(obj);
3442 if (was_unkn)
3443 more_experienced(0, 10);
3445 return;
3448 /* augment damage for a spell based on the hero's intelligence (and level) */
3450 spell_damage_bonus(
3451 int dmg) /* base amount to be adjusted by bonus or penalty */
3453 int intell = ACURR(A_INT);
3455 /* Punish low intelligence before low level else low intelligence
3456 gets punished only when high level */
3457 if (intell <= 9) {
3458 /* -3 penalty, but never reduce combined amount below 1
3459 (if dmg is 0 for some reason, we're careful to leave it there) */
3460 if (dmg > 1)
3461 dmg = (dmg <= 3) ? 1 : dmg - 3;
3462 } else if (intell <= 13 || u.ulevel < 5)
3463 ; /* no bonus or penalty; dmg remains same */
3464 else if (intell <= 18)
3465 dmg += 1;
3466 else if (intell <= 24 || u.ulevel < 14)
3467 dmg += 2;
3468 else
3469 dmg += 3; /* Int 25 */
3471 return dmg;
3475 * Generate the to hit bonus for a spell. Based on the hero's skill in
3476 * spell class and dexterity.
3478 staticfn int
3479 spell_hit_bonus(int skill)
3481 int hit_bon = 0;
3482 int dex = ACURR(A_DEX);
3484 switch (P_SKILL(spell_skilltype(skill))) {
3485 case P_ISRESTRICTED:
3486 case P_UNSKILLED:
3487 hit_bon = -4;
3488 break;
3489 case P_BASIC:
3490 hit_bon = 0;
3491 break;
3492 case P_SKILLED:
3493 hit_bon = 2;
3494 break;
3495 case P_EXPERT:
3496 hit_bon = 3;
3497 break;
3500 if (dex < 4)
3501 hit_bon -= 3;
3502 else if (dex < 6)
3503 hit_bon -= 2;
3504 else if (dex < 8)
3505 hit_bon -= 1;
3506 else if (dex < 14)
3507 /* Will change when print stuff below removed */
3508 hit_bon -= 0;
3509 else
3510 /* Even increment for dexterous heroes (see weapon.c abon) */
3511 hit_bon += dex - 14;
3513 return hit_bon;
3516 const char *
3517 exclam(int force)
3519 /* force == 0 occurs e.g. with sleep ray */
3520 /* note that large force is usual with wands so that !! would
3521 require information about hand/weapon/wand */
3522 return (const char *) ((force < 0) ? "?" : (force <= 4) ? "." : "!");
3525 void
3526 hit(
3527 const char *str, /* zap text or missile name */
3528 struct monst *mtmp, /* target; for missile, might be hero */
3529 const char *force) /* usually either "." or "!" via exclam() */
3531 boolean verbosely = (mtmp == &gy.youmonst
3532 || (flags.verbose
3533 && (cansee(gb.bhitpos.x, gb.bhitpos.y)
3534 || canspotmon(mtmp) || engulfing_u(mtmp))));
3536 pline("%s %s %s%s", The(str), vtense(str, "hit"),
3537 verbosely ? mon_nam(mtmp) : "it", force);
3540 void
3541 miss(const char *str, struct monst *mtmp)
3543 pline("%s %s %s.", The(str), vtense(str, "miss"),
3544 ((cansee(gb.bhitpos.x, gb.bhitpos.y) || canspotmon(mtmp))
3545 && flags.verbose) ? mon_nam(mtmp) : "it");
3548 staticfn void
3549 skiprange(int range, int *skipstart, int *skipend)
3551 int tr = (range / 4);
3552 int tmp = range - ((tr > 0) ? rnd(tr) : 0);
3554 *skipstart = tmp;
3555 *skipend = tmp - ((tmp / 4) * rnd(3));
3556 if (*skipend >= tmp)
3557 *skipend = tmp - 1;
3560 /* Maybe explode a trap hit by object otmp's effect;
3561 cancellation beam hitting a magical trap causes an explosion.
3562 Might delete the trap; won't destroy otmp. */
3563 staticfn void
3564 maybe_explode_trap(
3565 struct trap *ttmp,
3566 struct obj *otmp,
3567 boolean *learn_it)
3569 if (!ttmp || !otmp)
3570 return;
3571 if (otmp->otyp == WAN_CANCELLATION || otmp->otyp == SPE_CANCELLATION) {
3572 coordxy x = ttmp->tx, y = ttmp->ty;
3574 if (undestroyable_trap(ttmp->ttyp)) {
3575 shieldeff(x, y);
3576 if (cansee(x, y)) {
3577 ttmp->tseen = 1;
3578 newsym(x, y);
3579 *learn_it = TRUE;
3581 } else if (is_magical_trap(ttmp->ttyp)) {
3582 int seeit = cansee(x, y);
3584 /* note: this explosion mustn't destroy otmp */
3585 explode(x, y, -WAN_CANCELLATION,
3586 20 + d(3, 6), TRAP_EXPLODE, EXPL_MAGICAL);
3587 deltrap(ttmp);
3588 newsym(x, y);
3589 if (seeit)
3590 *learn_it = TRUE;
3595 /* zap_map() occurs before hitting monsters or objects and handles wands or
3596 spells that don't dish out 'elemental' damage */
3597 staticfn void
3598 zap_map(
3599 coordxy x, coordxy y,
3600 struct obj *obj) /* zapped wand, or book for cast spell */
3602 struct trap *ttmp = t_at(x, y);
3603 coordxy dbx = x, dby = y; /* might be changed by drawbridge handling */
3604 boolean learn_it = FALSE;
3607 * We handle drawbridge for lateral zaps; zap_updown() handles up/down.
3608 * Engravings only get hit by down zaps and we handle that here.
3611 /* cancellation */
3612 maybe_explode_trap(ttmp, obj, &learn_it);
3613 ttmp = t_at(x, y); /* refresh in case trap was altered or is gone */
3615 if (u.dz > 0) { /* zapping down */
3616 char ebuf[BUFSZ];
3617 struct engr *e = engr_at(x, y);
3619 /* subset of engraving effects; none sets `disclose' */
3620 if (e && e->engr_type != HEADSTONE) {
3621 switch (obj->otyp) {
3622 case WAN_POLYMORPH:
3623 case SPE_POLYMORPH:
3624 del_engr(e);
3625 make_engr_at(x, y, random_engraving(ebuf), svm.moves, 0);
3626 break;
3627 case WAN_CANCELLATION:
3628 case SPE_CANCELLATION:
3629 case WAN_MAKE_INVISIBLE:
3630 del_engr(e);
3631 break;
3632 case WAN_TELEPORTATION:
3633 case SPE_TELEPORT_AWAY:
3634 rloc_engr(e);
3635 break;
3636 case SPE_STONE_TO_FLESH:
3637 if (e->engr_type == ENGRAVE) {
3638 /* only affects things in stone */
3639 pline_The(Hallucination
3640 ? "floor runs like butter!"
3641 : "edges on the floor get smoother.");
3642 wipe_engr_at(x, y, d(2, 4), TRUE);
3644 break;
3645 case WAN_STRIKING:
3646 case SPE_FORCE_BOLT:
3647 wipe_engr_at(x, y, d(2, 4), TRUE);
3648 break;
3649 default:
3650 break;
3654 } else if (!u.dz) {
3655 int ltyp = levl[x][y].typ;
3657 if (find_drawbridge(&dbx, &dby)) {
3658 switch (obj->otyp) {
3659 case WAN_OPENING:
3660 case SPE_KNOCK:
3661 /* dbwall: 'closed door' of raised drawbridge */
3662 if (is_db_wall(x, y)) {
3663 if (cansee(dbx, dby) || cansee(x, y))
3664 learn_it = TRUE;
3665 open_drawbridge(dbx, dby);
3667 break;
3668 case WAN_LOCKING:
3669 case SPE_WIZARD_LOCK:
3670 /* drawbridge_down: span of lowered drawbridge */
3671 if ((cansee(dbx, dby) || cansee(x, y))
3672 && levl[dbx][dby].typ == DRAWBRIDGE_DOWN)
3673 learn_it = TRUE;
3674 close_drawbridge(dbx, dby);
3675 break;
3676 case WAN_STRIKING:
3677 case SPE_FORCE_BOLT:
3678 /* !drawbridge_up: not spot in front of raised bridge,
3679 so either span of lowered bridge or portcullis */
3680 if (ltyp != DRAWBRIDGE_UP) {
3681 learn_it = TRUE;
3682 destroy_drawbridge(dbx, dby);
3684 break;
3686 } /* find_drawbridge */
3687 } /* !u.uz */
3689 if (obj->otyp == WAN_PROBING) {
3691 * Probing, either up/down or lateral.
3693 schar ltyp;
3694 int oldtyp, oldglyph;
3696 /* map terrain; might reveal a special room which is already within
3697 view that hasn't been entered yet */
3698 oldtyp = svl.lastseentyp[x][y];
3699 oldglyph = glyph_at(x, y);
3700 show_map_spot(x, y, FALSE);
3701 if (oldtyp != svl.lastseentyp[x][y] || oldglyph != glyph_at(x, y)) {
3702 /* TODO: ought to give some message */
3703 learn_it = TRUE;
3705 ltyp = SURFACE_AT(x, y);
3706 /* secret door gets revealed, converted into regular door */
3707 if (ltyp == SDOOR) {
3708 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
3709 recalc_block_point(x, y);
3710 newsym(x, y);
3711 if (cansee(x, y)) {
3712 pline("Probing reveals a secret door.");
3713 learn_it = TRUE;
3714 } else if (Is_rogue_level(&u.uz)) { /* from zap_over_floor() */
3715 draft_message(FALSE); /* "You feel a draft." (open doorway) */
3718 /* secret corridor likewise, although only ones within view will
3719 still be secret; for the !cansee(x,y) case, show_map_spot()
3720 above has already converted the spot to regular corridor */
3721 } else if (ltyp == SCORR) {
3722 levl[x][y].typ = CORR;
3723 unblock_point(x, y);
3724 newsym(x, y);
3725 pline("Probing exposes a secret corridor.");
3726 learn_it = TRUE;
3728 /* if on or over ice, describe it ("solid ice", "thin ice", &c);
3729 likewise for furniture in case hero is levitating while blind */
3730 } else if (ltyp == ICE || IS_FURNITURE(ltyp)) {
3731 if (u.dz > 0) { /* down, which also means x,y == u.ux,u.uy */
3732 force_decor(TRUE);
3733 learn_it = TRUE;
3737 * Probing reveals undiscovered traps.
3739 * FIXME? This finds floor traps even when zapping up and
3740 * ceiling traps even when zapping down.
3742 if (ttmp) {
3743 const char *ttmpname;
3744 unsigned t_already_seen = ttmp->tseen;
3745 boolean use_the, hallu = Hallucination != 0;
3747 /* should probably be changed to use sense_trap(detect.c)
3748 so that trap can temporarily be forced to be shown and
3749 map browsing can take place before it reverts to being
3750 covered by monster or object(s) */
3751 ttmp->tseen = 1;
3752 newsym(x, y);
3754 if (!t_already_seen || hallu) {
3755 ttmpname = trapname(ttmp->ttyp, FALSE);
3756 use_the = !hallu ? (ttmp->ttyp == VIBRATING_SQUARE
3757 && Invocation_lev(&u.uz))
3758 : !rn2(4);
3759 You("find %s%c",
3760 use_the ? the(ttmpname) : an(ttmpname),
3761 use_the ? '!' : '.');
3762 learn_it = !hallu;
3764 } /* t_at() */
3765 } /* probing */
3767 if (learn_it)
3768 learnwand(obj);
3769 return;
3773 * Called for the following distance effects:
3774 * when a weapon is thrown (weapon == THROWN_WEAPON)
3775 * when an object is kicked (KICKED_WEAPON)
3776 * when an IMMEDIATE wand is zapped (ZAPPED_WAND)
3777 * when a light beam is flashed (FLASHED_LIGHT)
3778 * when a mirror is applied (INVIS_BEAM)
3779 * A thrown/kicked object falls down at end of its range or when a monster
3780 * is hit. The variable 'gb.bhitpos' is set to the final position of the
3781 * weapon thrown/zapped. The ray of a wand may affect (by calling a provided
3782 * function) several objects and monsters on its path. The return value
3783 * is the monster hit (weapon != ZAPPED_WAND), or a null monster pointer.
3785 * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be
3786 * destroyed and *pobj set to NULL to indicate this.
3788 * Check !u.uswallow before calling bhit().
3789 * This function reveals the absence of a remembered invisible monster in
3790 * necessary cases (throwing or kicking weapons). The presence of a real
3791 * one is revealed for a weapon, but if not a weapon is left up to fhitm().
3793 * If fhitm returns non-zero value, stops the beam and returns the monster
3795 struct monst *
3796 bhit(
3797 coordxy ddx, coordxy ddy, int range, /* direction and range */
3798 enum bhit_call_types weapon, /* defined in hack.h */
3799 int (*fhitm)(MONST_P, OBJ_P), /* fns called when mon/obj hit */
3800 int (*fhito)(OBJ_P, OBJ_P),
3801 struct obj **pobj) /* object tossed/used, set to NULL
3802 * if object is destroyed */
3804 struct monst *mtmp, *result = (struct monst *) 0;
3805 struct obj *obj = *pobj;
3806 struct trap *ttmp;
3807 uchar typ;
3808 boolean shopdoor = FALSE, point_blank = TRUE;
3809 boolean in_skip = FALSE, allow_skip = FALSE;
3810 boolean tethered_weapon = FALSE;
3811 int skiprange_start = 0, skiprange_end = 0, skipcount = 0;
3812 struct obj *was_returning =
3813 (iflags.returning_missile == obj) ? obj : (struct obj *) 0;
3815 if (weapon == KICKED_WEAPON) {
3816 /* object starts one square in front of player */
3817 gb.bhitpos.x = u.ux + ddx;
3818 gb.bhitpos.y = u.uy + ddy;
3819 range--;
3820 } else {
3821 gb.bhitpos.x = u.ux;
3822 gb.bhitpos.y = u.uy;
3825 if (weapon == THROWN_WEAPON && obj && obj->otyp == ROCK) {
3826 skiprange(range, &skiprange_start, &skiprange_end);
3827 allow_skip = !rn2(3);
3830 if (weapon == FLASHED_LIGHT) {
3831 tmp_at(DISP_BEAM, cmap_to_glyph(S_flashbeam));
3832 } else if (weapon == THROWN_TETHERED_WEAPON && obj) {
3833 tethered_weapon = TRUE;
3834 weapon = THROWN_WEAPON; /* simplify 'if's that follow below */
3835 tmp_at(DISP_TETHER, obj_to_glyph(obj, rn2_on_display_rng));
3836 } else if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
3837 tmp_at(DISP_FLASH, obj_to_glyph(obj, rn2_on_display_rng));
3839 while (range-- > 0) {
3840 coordxy x, y;
3842 gb.bhitpos.x += ddx;
3843 gb.bhitpos.y += ddy;
3844 x = gb.bhitpos.x;
3845 y = gb.bhitpos.y;
3847 if (!isok(x, y)) {
3848 gb.bhitpos.x -= ddx;
3849 gb.bhitpos.y -= ddy;
3850 break;
3853 if (is_pick(obj) && inside_shop(x, y)
3854 && (mtmp = shkcatch(obj, x, y)) != 0) {
3855 tmp_at(DISP_END, 0);
3856 result = mtmp;
3857 goto bhit_done;
3860 typ = levl[x][y].typ;
3862 /* WATER aka "wall of water" stops items */
3863 if (IS_WATERWALL(typ) || typ == LAVAWALL) {
3864 if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
3865 break;
3868 /* iron bars will block anything big enough and break some things */
3869 if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) {
3870 if (obj->lamplit && !Blind)
3871 show_transient_light(obj, x, y);
3872 if (typ == IRONBARS
3873 && hits_bars(pobj, x - ddx, y - ddy, x, y,
3874 point_blank ? 0 : !rn2(5), 1)) {
3875 /* caveat: obj might now be null... */
3876 obj = *pobj; /* not currently needed due to 'break'; keep */
3877 nhUse(obj); /* in case usage gets added after the loop */
3878 gb.bhitpos.x -= ddx;
3879 gb.bhitpos.y -= ddy;
3880 break;
3882 } else if (weapon == FLASHED_LIGHT) {
3883 if (!Blind)
3884 show_transient_light((struct obj *) 0, x, y);
3887 if (weapon == ZAPPED_WAND) {
3888 /* cancellation/opening/locking/striking/probing */
3889 zap_map(x, y, obj);
3890 /* terrain might have changed (exposed secret door|corridor) */
3891 typ = levl[x][y].typ;
3894 mtmp = m_at(x, y);
3895 ttmp = t_at(x, y);
3896 if (!mtmp && ttmp && (ttmp->ttyp == WEB)
3897 && (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
3898 && !rn2(3)) {
3899 if (cansee(x, y)) {
3900 pline("%s gets stuck in a web!", Yname2(obj));
3901 ttmp->tseen = TRUE;
3902 newsym(x, y);
3904 if (was_returning)
3905 iflags.returning_missile = (genericptr_t) 0;
3906 break;
3910 * skipping rocks
3912 * skiprange_start is only set if this is a thrown rock
3914 if (skiprange_start && (range == skiprange_start) && allow_skip) {
3915 if (is_pool(x, y) && !mtmp) {
3916 in_skip = TRUE;
3917 if (!Blind)
3918 pline("%s %s%s.", Yname2(obj), otense(obj, "skip"),
3919 skipcount ? " again" : "");
3920 else
3921 You_hear("%s skip.", yname(obj));
3922 skipcount++;
3923 } else if (skiprange_start > skiprange_end + 1) {
3924 --skiprange_start;
3927 if (in_skip) {
3928 if (range <= skiprange_end) {
3929 in_skip = FALSE;
3930 if (range > 3) /* another bounce? */
3931 skiprange(range, &skiprange_start, &skiprange_end);
3932 } else if (mtmp && M_IN_WATER(mtmp->data)) {
3933 if (!Blind && canspotmon(mtmp))
3934 pline("%s %s over %s.", Yname2(obj), otense(obj, "pass"),
3935 mon_nam(mtmp));
3936 mtmp = (struct monst *) 0;
3940 /* if mtmp is a shade and missile passes harmlessly through it,
3941 give message and skip it in order to keep going;
3942 if attack is light and mtmp is a mimic pretending to be an
3943 object, behave as if there is no monster here (if pretending
3944 to be furniture, it will be revealed by flash_hits_mon()) */
3945 if (mtmp && (((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
3946 && shade_miss(&gy.youmonst, mtmp, obj, TRUE, TRUE))
3947 || (weapon == FLASHED_LIGHT
3948 && M_AP_TYPE(mtmp) == M_AP_OBJECT)))
3949 mtmp = (struct monst *) 0;
3951 if (mtmp) {
3952 gn.notonhead = (x != mtmp->mx || y != mtmp->my);
3953 if (weapon == FLASHED_LIGHT) {
3954 /* FLASHED_LIGHT hitting invisible monster should pass
3955 through instead of stop so we call flash_hits_mon()
3956 directly rather than returning mtmp back to caller.
3957 That allows the flash to keep on going. Note that we
3958 use mtmp->minvis not canspotmon() because it makes no
3959 difference whether hero can see the monster or not. */
3960 if (mtmp->minvis) {
3961 obj->ox = u.ux, obj->oy = u.uy;
3962 (void) flash_hits_mon(mtmp, obj);
3963 } else {
3964 tmp_at(DISP_END, 0);
3965 result = mtmp; /* caller will call flash_hits_mon() */
3966 goto bhit_done;
3968 } else if (weapon == INVIS_BEAM) {
3969 /* Like FLASHED_LIGHT, INVIS_BEAM should continue
3970 through invisible targets; unlike it, we aren't
3971 prepared for multiple hits so just get first one
3972 that's either visible or could see its invisible
3973 self. [No tmp_at() cleanup is needed here.] */
3974 if (!mtmp->minvis || perceives(mtmp->data)) {
3975 result = mtmp;
3976 goto bhit_done;
3978 } else if (weapon != ZAPPED_WAND) {
3979 /* THROWN_WEAPON, KICKED_WEAPON */
3980 if (!tethered_weapon)
3981 tmp_at(DISP_END, 0);
3983 if (cansee(x, y) && !canspotmon(mtmp))
3984 map_invisible(x, y);
3985 result = mtmp;
3986 goto bhit_done;
3987 } else {
3988 /* ZAPPED_WAND */
3989 if ((*fhitm)(mtmp, obj)) {
3990 result = mtmp;
3991 goto bhit_done;
3993 range -= 3;
3995 } else {
3996 if (weapon == ZAPPED_WAND && obj->otyp == WAN_PROBING
3997 && glyph_is_invisible(levl[x][y].glyph)) {
3998 unmap_object(x, y);
3999 newsym(x, y);
4002 if (fhito) {
4003 if (bhitpile(obj, fhito, x, y, 0))
4004 range--;
4005 } else {
4006 if (weapon == KICKED_WEAPON
4007 && ((obj->oclass == COIN_CLASS && OBJ_AT(x, y))
4008 || ship_object(obj, x, y, costly_spot(x, y)))) {
4009 tmp_at(DISP_END, 0);
4010 goto bhit_done; /* result == (struct monst *) 0 */
4013 if (weapon == ZAPPED_WAND && (IS_DOOR(typ) || typ == SDOOR)) {
4014 switch (obj->otyp) {
4015 case WAN_OPENING:
4016 case WAN_LOCKING:
4017 case WAN_STRIKING:
4018 case SPE_KNOCK:
4019 case SPE_WIZARD_LOCK:
4020 case SPE_FORCE_BOLT:
4021 if (doorlock(obj, x, y)) {
4022 if (cansee(x, y) || (obj->otyp == WAN_STRIKING && !Deaf))
4023 learnwand(obj);
4024 if (levl[x][y].doormask == D_BROKEN
4025 && *in_rooms(x, y, SHOPBASE)) {
4026 shopdoor = TRUE;
4027 add_damage(x, y, SHOP_DOOR_COST);
4030 break;
4033 if (!ZAP_POS(typ) || closed_door(x, y)) {
4034 gb.bhitpos.x -= ddx;
4035 gb.bhitpos.y -= ddy;
4036 break;
4038 if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) {
4039 /* 'I' present but no monster: erase */
4040 /* do this before the tmp_at() */
4041 if (glyph_is_invisible(levl[x][y].glyph) && cansee(x, y)) {
4042 unmap_object(x, y);
4043 newsym(x, y);
4045 tmp_at(x, y);
4046 nh_delay_output();
4047 /* kicked objects fall in pools */
4048 if ((weapon == KICKED_WEAPON) && is_pool_or_lava(x, y))
4049 break;
4050 if (IS_SINK(typ) && weapon != FLASHED_LIGHT)
4051 break; /* physical objects fall onto sink */
4053 /* limit range of ball so hero won't make an invalid move */
4054 if (weapon == THROWN_WEAPON && range > 0
4055 && obj->otyp == HEAVY_IRON_BALL) {
4056 struct obj *bobj;
4057 struct trap *t;
4059 if ((bobj = sobj_at(BOULDER, x, y)) != 0) {
4060 if (cansee(x, y))
4061 pline("%s hits %s.", The(distant_name(obj, xname)),
4062 an(xname(bobj)));
4063 range = 0;
4064 } else if (obj == uball) {
4065 if (!test_move(x - ddx, y - ddy, ddx, ddy, TEST_MOVE)) {
4066 /* nb: it didn't hit anything directly */
4067 if (cansee(x, y))
4068 pline("%s jerks to an abrupt halt.",
4069 The(distant_name(obj, xname))); /* lame */
4070 range = 0;
4071 } else if (Sokoban && (t = t_at(x, y)) != 0
4072 && (is_pit(t->ttyp) || is_hole(t->ttyp))) {
4073 /* hero falls into the trap, so ball stops */
4074 range = 0;
4079 /* thrown/kicked missile has moved away from its starting spot */
4080 point_blank = FALSE; /* affects passing through iron bars */
4083 if ((weapon != ZAPPED_WAND && weapon != INVIS_BEAM && !tethered_weapon)
4084 || (was_returning && was_returning != iflags.returning_missile))
4085 tmp_at(DISP_END, 0);
4087 if (shopdoor)
4088 pay_for_damage("destroy", FALSE);
4090 bhit_done:
4091 /* note: for FLASHED_LIGHT, _caller_ must call transient_light_cleanup()
4092 after possibly calling flash_hits_mon() */
4093 if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
4094 transient_light_cleanup();
4096 return result;
4099 /* process thrown boomerang, which travels a curving path...
4100 * A multi-shot volley ought to have all missiles in flight at once,
4101 * but we're called separately for each one. We terminate the volley
4102 * early on a failed catch since continuing to throw after being hit
4103 * is too obviously silly.
4105 struct monst *
4106 boomhit(struct obj *obj, coordxy dx, coordxy dy)
4108 int i, ct;
4109 int boom; /* showsym[] index */
4110 struct monst *mtmp;
4111 boolean counterclockwise = URIGHTY; /* ULEFTY => clockwise */
4113 /* counterclockwise traversal patterns, from @ to 1 then on through to 9
4114 * ..........................54.................................
4115 * ..................43.....6..3....765.........................
4116 * ..........32.....5..2...7...2...8...4....87..................
4117 * .........4..1....6..1...8..1....9...3...9..6.....98..........
4118 * ..21@....5...@...7..@....9@......@12....@...5...@..7.....@9..
4119 * .3...9....6..9....89.....................1..4...1..6....1..8.
4120 * .4...8.....78.............................23....2..5...2...7.
4121 * ..567............................................34....3..6..
4122 * ........................................................45...
4123 * (invert rows for corresponding clockwise patterns)
4126 gb.bhitpos.x = u.ux;
4127 gb.bhitpos.y = u.uy;
4128 boom = counterclockwise ? S_boomleft : S_boomright;
4129 i = xytod(dx, dy);
4130 tmp_at(DISP_FLASH, cmap_to_glyph(boom));
4131 for (ct = 0; ct < 10; ct++) {
4132 i = DIR_CLAMP(i);
4133 boom = (S_boomleft + S_boomright - boom); /* toggle */
4134 tmp_at(DISP_CHANGE, cmap_to_glyph(boom)); /* change glyph */
4135 dx = xdir[i];
4136 dy = ydir[i];
4137 gb.bhitpos.x += dx;
4138 gb.bhitpos.y += dy;
4139 if (!isok(gb.bhitpos.x, gb.bhitpos.y)) {
4140 gb.bhitpos.x -= dx;
4141 gb.bhitpos.y -= dy;
4142 break;
4144 if ((mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y)) != 0) {
4145 m_respond(mtmp);
4146 tmp_at(DISP_END, 0);
4147 return mtmp;
4149 if (!ZAP_POS(levl[gb.bhitpos.x][gb.bhitpos.y].typ)
4150 || closed_door(gb.bhitpos.x, gb.bhitpos.y)) {
4151 gb.bhitpos.x -= dx;
4152 gb.bhitpos.y -= dy;
4153 break;
4155 if (u_at(gb.bhitpos.x, gb.bhitpos.y)) { /* ct == 9 */
4156 if (Fumbling || rn2(20) >= ACURR(A_DEX)) {
4157 /* we hit ourselves */
4158 (void) thitu(10 + obj->spe, dmgval(obj, &gy.youmonst), &obj,
4159 "boomerang");
4160 endmultishot(TRUE);
4161 break;
4162 } else { /* we catch it */
4163 tmp_at(DISP_END, 0);
4164 You("skillfully catch the boomerang.");
4165 return &gy.youmonst;
4168 tmp_at(gb.bhitpos.x, gb.bhitpos.y);
4169 nh_delay_output();
4170 if (IS_SINK(levl[gb.bhitpos.x][gb.bhitpos.y].typ)) {
4171 Soundeffect(se_boomerang_klonk, 75);
4172 if (!Deaf)
4173 pline("Klonk!");
4174 wake_nearto(gb.bhitpos.x, gb.bhitpos.y, 20);
4175 break; /* boomerang falls on sink */
4177 /* ct==0, initial position, we want next delta to be same;
4178 ct==5, opposite position, repeat delta undoes first one */
4179 if (ct % 5 != 0)
4180 i = counterclockwise ? DIR_LEFT(i) : DIR_RIGHT(i);
4182 tmp_at(DISP_END, 0); /* do not leave last symbol */
4183 return (struct monst *) 0;
4186 /* used by buzz(); also used by munslime(muse.c); returns damage applied
4187 to mon; note: caller is responsible for killing mon if damage is fatal */
4189 zhitm(
4190 struct monst *mon, /* monster being hit */
4191 int type, /* zap or breath type */
4192 int nd, /* number of hit dice to use */
4193 struct obj **ootmp) /* to return worn armor for caller to disintegrate */
4195 int tmp = 0, orig_dmg = 0; /* damage amount */
4196 int damgtype = zaptype(type) % 10;
4197 boolean sho_shieldeff = FALSE;
4198 boolean spellcaster = is_hero_spell(type); /* maybe get a bonus! */
4200 *ootmp = (struct obj *) 0;
4201 switch (damgtype) {
4202 case ZT_MAGIC_MISSILE:
4203 if (resists_magm(mon) || defended(mon, AD_MAGM)) {
4204 sho_shieldeff = TRUE;
4205 break;
4207 tmp = d(nd, 6);
4208 if (spellcaster)
4209 tmp = spell_damage_bonus(tmp);
4210 break;
4211 case ZT_FIRE:
4212 if (resists_fire(mon) || defended(mon, AD_FIRE)) {
4213 sho_shieldeff = TRUE;
4214 break;
4216 tmp = d(nd, 6);
4217 if (spellcaster)
4218 tmp = spell_damage_bonus(tmp);
4219 orig_dmg = tmp; /* includes spell bonus but not monster vuln to fire */
4220 if (resists_cold(mon))
4221 tmp += 7;
4222 if (burnarmor(mon)) {
4223 if (!rn2(3)) {
4224 tmp += destroy_items(mon, AD_FIRE, orig_dmg);
4225 ignite_items(mon->minvent);
4228 break;
4229 case ZT_COLD:
4230 if (resists_cold(mon) || defended(mon, AD_COLD)) {
4231 sho_shieldeff = TRUE;
4232 break;
4234 tmp = d(nd, 6);
4235 if (spellcaster)
4236 tmp = spell_damage_bonus(tmp);
4237 orig_dmg = tmp; /* includes spell bonus but not monster vuln to cold */
4238 if (resists_fire(mon))
4239 tmp += d(nd, 3);
4240 if (!rn2(3))
4241 tmp += destroy_items(mon, AD_COLD, orig_dmg);
4242 break;
4243 case ZT_SLEEP:
4244 /* resistance and shield effect and revealing concealed mimic are
4245 handled by sleep_monst() */
4246 tmp = 0;
4247 (void) sleep_monst(mon, d(nd, 25),
4248 type == ZT_WAND(ZT_SLEEP) ? WAND_CLASS : '\0');
4249 break;
4250 case ZT_DEATH: /* death/disintegration */
4251 if (abs(type) != ZT_BREATH(ZT_DEATH)) { /* death */
4252 if (mon->data == &mons[PM_DEATH]) {
4253 healmon(mon, mon->mhpmax * 3 / 2, mon->mhpmax / 2);
4254 if (mon->mhpmax >= MAGIC_COOKIE)
4255 mon->mhpmax = MAGIC_COOKIE - 1;
4256 tmp = 0;
4257 break;
4259 if (nonliving(mon->data) || is_demon(mon->data)
4260 || is_vampshifter(mon) || resists_magm(mon)) {
4261 /* similar to player */
4262 sho_shieldeff = TRUE;
4263 break;
4265 type = -1; /* so they don't get saving throws */
4266 } else {
4267 struct obj *otmp2;
4269 if (resists_disint(mon) || defended(mon, AD_DISN)) {
4270 sho_shieldeff = TRUE;
4271 } else if (mon->misc_worn_check & W_ARMS) {
4272 /* destroy shield; victim survives */
4273 *ootmp = which_armor(mon, W_ARMS);
4274 } else if (mon->misc_worn_check & W_ARM) {
4275 /* destroy suit, also cloak if present */
4276 *ootmp = which_armor(mon, W_ARM);
4277 if ((otmp2 = which_armor(mon, W_ARMC)) != 0)
4278 m_useup(mon, otmp2);
4279 } else {
4280 /* no suit, victim dies; destroy cloak
4281 and shirt now in case target gets life-saved */
4282 tmp = MAGIC_COOKIE;
4283 if ((otmp2 = which_armor(mon, W_ARMC)) != 0)
4284 m_useup(mon, otmp2);
4285 if ((otmp2 = which_armor(mon, W_ARMU)) != 0)
4286 m_useup(mon, otmp2);
4288 type = -1; /* no saving throw wanted */
4289 break; /* not ordinary damage */
4291 tmp = mon->mhp + 1;
4292 break;
4293 case ZT_LIGHTNING:
4294 tmp = d(nd, 6);
4295 if (spellcaster)
4296 tmp = spell_damage_bonus(tmp);
4297 orig_dmg = tmp;
4298 if (resists_elec(mon) || defended(mon, AD_ELEC)) {
4299 sho_shieldeff = TRUE;
4300 tmp = 0;
4301 /* can still blind the monster */
4303 if (!resists_blnd(mon)
4304 && !(type > 0 && engulfing_u(mon))
4305 && nd > 2) {
4306 /* sufficiently powerful lightning blinds monsters */
4307 unsigned rnd_tmp = rnd(50);
4308 mon->mcansee = 0;
4309 if ((mon->mblinded + rnd_tmp) > 127)
4310 mon->mblinded = 127;
4311 else
4312 mon->mblinded += rnd_tmp;
4314 if (!rn2(3))
4315 tmp += destroy_items(mon, AD_ELEC, orig_dmg);
4316 break;
4317 case ZT_POISON_GAS:
4318 if (resists_poison(mon) || defended(mon, AD_DRST)) {
4319 sho_shieldeff = TRUE;
4320 break;
4322 tmp = d(nd, 6);
4323 break;
4324 case ZT_ACID:
4325 if (resists_acid(mon) || defended(mon, AD_ACID)) {
4326 sho_shieldeff = TRUE;
4327 break;
4329 tmp = d(nd, 6);
4330 if (!rn2(6))
4331 acid_damage(MON_WEP(mon));
4332 if (!rn2(6))
4333 erode_armor(mon, ERODE_CORRODE);
4334 break;
4336 if (sho_shieldeff)
4337 shieldeff(mon->mx, mon->my);
4338 if (is_hero_spell(type) && (Role_if(PM_KNIGHT) && u.uhave.questart))
4339 tmp *= 2;
4340 if (tmp > 0 && type >= 0
4341 && resist(mon, type < ZT_SPELL(0) ? WAND_CLASS : '\0', 0, NOTELL))
4342 tmp /= 2;
4343 if (tmp < 0)
4344 tmp = 0; /* don't allow negative damage */
4345 debugpline3("zapped monster hp = %d (= %d - %d)", mon->mhp - tmp,
4346 mon->mhp, tmp);
4347 mon->mhp -= tmp;
4348 return tmp;
4351 staticfn void
4352 zhitu(
4353 int type, int nd,
4354 const char *fltxt,
4355 coordxy sx, coordxy sy)
4357 int dam = 0, abstyp = zaptype(type);
4358 int orig_dam = 0;
4360 switch (abstyp % 10) {
4361 case ZT_MAGIC_MISSILE:
4362 if (Antimagic) {
4363 shieldeff(sx, sy);
4364 pline_The("missiles bounce off!");
4365 monstseesu(M_SEEN_MAGR);
4366 } else {
4367 dam = d(nd, 6);
4368 exercise(A_STR, FALSE);
4369 monstunseesu(M_SEEN_MAGR);
4371 break;
4372 case ZT_FIRE:
4373 orig_dam = d(nd, 6);
4374 if (Fire_resistance) {
4375 shieldeff(sx, sy);
4376 You("don't feel hot!");
4377 monstseesu(M_SEEN_FIRE);
4378 ugolemeffects(AD_FIRE, orig_dam);
4379 } else {
4380 dam = orig_dam;
4381 monstunseesu(M_SEEN_FIRE);
4383 burn_away_slime();
4384 if (burnarmor(&gy.youmonst)) { /* "body hit" */
4385 if (!rn2(3))
4386 (void) destroy_items(&gy.youmonst, AD_FIRE, orig_dam);
4387 if (!rn2(3))
4388 ignite_items(gi.invent);
4390 break;
4391 case ZT_COLD:
4392 orig_dam = d(nd, 6);
4393 if (Cold_resistance) {
4394 shieldeff(sx, sy);
4395 You("don't feel cold.");
4396 monstseesu(M_SEEN_COLD);
4397 ugolemeffects(AD_COLD, orig_dam);
4398 } else {
4399 dam = orig_dam;
4400 monstunseesu(M_SEEN_COLD);
4402 if (!rn2(3))
4403 (void) destroy_items(&gy.youmonst, AD_COLD, orig_dam);
4404 break;
4405 case ZT_SLEEP:
4406 if (Sleep_resistance) {
4407 shieldeff(u.ux, u.uy);
4408 You("don't feel sleepy.");
4409 monstseesu(M_SEEN_SLEEP);
4410 } else {
4411 monstunseesu(M_SEEN_SLEEP);
4412 fall_asleep(-d(nd, 25), TRUE); /* sleep ray */
4414 break;
4415 case ZT_DEATH:
4416 if (abstyp == ZT_BREATH(ZT_DEATH)) {
4417 boolean disn_prot = inventory_resistance_check(AD_DISN);
4419 if (Disint_resistance) {
4420 You("are not disintegrated.");
4421 monstseesu(M_SEEN_DISINT);
4422 break;
4423 } else if (disn_prot) {
4424 break;
4426 monstunseesu(M_SEEN_DISINT);
4427 if (uarms) {
4428 /* destroy shield; other possessions are safe */
4429 (void) destroy_arm(uarms);
4430 break;
4431 } else if (uarm) {
4432 /* destroy suit; if present, cloak goes too */
4433 if (uarmc)
4434 (void) destroy_arm(uarmc);
4435 (void) destroy_arm(uarm);
4436 break;
4438 /* no shield or suit, you're dead; wipe out cloak
4439 and/or shirt in case of life-saving or bones */
4440 if (uarmc)
4441 (void) destroy_arm(uarmc);
4442 if (uarmu)
4443 (void) destroy_arm(uarmu);
4444 } else if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) {
4445 shieldeff(sx, sy);
4446 You("seem unaffected.");
4447 break;
4448 } else if (Antimagic) {
4449 shieldeff(sx, sy);
4450 monstseesu(M_SEEN_MAGR);
4451 You("aren't affected.");
4452 break;
4454 monstunseesu(M_SEEN_MAGR);
4455 svk.killer.format = KILLED_BY_AN;
4456 Strcpy(svk.killer.name, fltxt ? fltxt : "");
4457 /* when killed by disintegration breath, don't leave corpse */
4458 u.ugrave_arise = (type == -ZT_BREATH(ZT_DEATH)) ? -3 : NON_PM;
4459 done(DIED);
4460 return; /* lifesaved */
4461 case ZT_LIGHTNING:
4462 orig_dam = d(nd, 6);
4463 if (Shock_resistance) {
4464 shieldeff(sx, sy);
4465 You("aren't affected.");
4466 monstseesu(M_SEEN_ELEC);
4467 ugolemeffects(AD_ELEC, orig_dam);
4468 } else {
4469 dam = orig_dam;
4470 exercise(A_CON, FALSE);
4471 monstunseesu(M_SEEN_ELEC);
4473 if (!rn2(3))
4474 (void) destroy_items(&gy.youmonst, AD_ELEC, orig_dam);
4475 break;
4476 case ZT_POISON_GAS:
4477 poisoned("blast", A_DEX, "poisoned blast", 15, FALSE);
4478 break;
4479 case ZT_ACID:
4480 if (Acid_resistance) {
4481 pline_The("%s doesn't hurt.", hliquid("acid"));
4482 monstseesu(M_SEEN_ACID);
4483 dam = 0;
4484 } else {
4485 pline_The("%s burns!", hliquid("acid"));
4486 dam = d(nd, 6);
4487 exercise(A_STR, FALSE);
4488 monstunseesu(M_SEEN_ACID);
4490 /* using two weapons at once makes both of them more vulnerable */
4491 if (!rn2(u.twoweap ? 3 : 6))
4492 acid_damage(uwep);
4493 if (u.twoweap && !rn2(3))
4494 acid_damage(uswapwep);
4495 if (!rn2(6))
4496 erode_armor(&gy.youmonst, ERODE_CORRODE);
4497 break;
4501 * 3.7: when fatal, this used to yield "Killed by <fltxt>." without any
4502 * information about who was responsible. Now 'buzzer' is used to try
4503 * to supply "zapped/cast/breathed by <mon> [imitating <other_mon>]."
4505 * Room for improvement: there is no monster available when player is
4506 * hit by divine lighting or by Plane of Air thunderstorm so cause of
4507 * death remains "killed by a bolt of lightning" w/o extra explanation.
4509 * Wand of death, spell of finger of death, and disintegration breath
4510 * don't use this routine so don't include 'inflicted by'.
4513 char kbuf[BUFSZ];
4514 struct obj *otmp = gc.current_wand;
4515 /* fire horn and frost horn get handled as wands by caller */
4516 const char *verb = (abstyp < 10) /* wand */
4517 ? ((otmp && otmp->oclass == TOOL_CLASS) ? "played"
4518 : "zapped")
4519 : (abstyp < 20) ? "cast"
4520 : (abstyp < 30) ? "exhaled"
4521 : "imagined"; /* should never happen */
4523 if (type < 0 || (type == 0 && gb.buzzer != 0)) {
4524 /* if gb.buzzer is Null, kbuf[] will end up with just <fltxt> */
4525 (void) death_inflicted_by(kbuf, fltxt, gb.buzzer);
4526 /* change "death inflicted by mon" to "death <verb> by mon" */
4527 if (gb.buzzer)
4528 (void) strsubst(kbuf, "inflicted", verb);
4529 } else {
4530 /* FIXME: "zapped by herself" is suitable for a rebound;
4531 "zapped at herself" would be better if player explicitly
4532 targeted hero */
4533 Sprintf(kbuf, "%s %s by %sself", fltxt, verb, uhim());
4535 /* Half_spell_damage protection yields half-damage for wands & spells,
4536 including hero's own ricochets; breath attacks do full damage */
4537 if (dam && Half_spell_damage && abstyp < 20)
4538 dam = (dam + 1) / 2;
4539 losehp(dam, kbuf, KILLED_BY_AN);
4541 return;
4545 * burn objects (such as scrolls and spellbooks) on floor
4546 * at position x,y; return the number of objects burned
4549 burn_floor_objects(
4550 coordxy x, coordxy y,
4551 boolean give_feedback, /* caller needs to decide about visibility checks */
4552 boolean u_caused)
4554 struct obj *obj, *obj2;
4555 long i, scrquan, delquan;
4556 char buf1[BUFSZ], buf2[BUFSZ];
4557 int cnt = 0;
4559 for (obj = svl.level.objects[x][y]; obj; obj = obj2) {
4560 obj2 = obj->nexthere;
4561 if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS
4562 || (obj->oclass == FOOD_CLASS
4563 && obj->otyp == GLOB_OF_GREEN_SLIME)) {
4564 if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL
4565 || obj_resists(obj, 2, 100))
4566 continue;
4567 scrquan = obj->quan; /* number present */
4568 delquan = 0L; /* number to destroy */
4569 for (i = scrquan; i > 0L; i--)
4570 if (!rn2(3))
4571 delquan++;
4572 if (delquan) {
4573 /* save name before potential delobj() */
4574 if (give_feedback) {
4575 obj->quan = 1L;
4576 Strcpy(buf1, u_at(x, y)
4577 ? xname(obj)
4578 : distant_name(obj, xname));
4579 obj->quan = 2L;
4580 Strcpy(buf2, u_at(x, y)
4581 ? xname(obj)
4582 : distant_name(obj, xname));
4583 obj->quan = scrquan;
4585 /* useupf(), which charges, only if hero caused damage */
4586 if (u_caused)
4587 useupf(obj, delquan);
4588 else if (delquan < scrquan) {
4589 obj->quan -= delquan;
4590 obj->owt = weight(obj);
4591 } else
4592 delobj(obj);
4593 cnt += delquan;
4594 if (give_feedback) {
4595 if (delquan > 1L)
4596 pline("%ld %s burn.", delquan, buf2);
4597 else
4598 pline("%s burns.", An(buf1));
4603 /* This also ignites floor items, but does not change cnt
4604 because they weren't consumed. */
4605 ignite_items(svl.level.objects[x][y]);
4606 return cnt;
4609 /* will zap/spell/breath attack score a hit against armor class `ac'? */
4610 staticfn int
4611 zap_hit(
4612 int ac,
4613 int type) /* either hero cast spell type or 0 */
4615 int chance = rn2(20);
4616 int spell_bonus = type ? spell_hit_bonus(type) : 0;
4618 /* small chance for naked target to avoid being hit */
4619 if (!chance)
4620 return rnd(10) < ac + spell_bonus;
4622 /* very high armor protection does not achieve invulnerability */
4623 ac = AC_VALUE(ac);
4625 return (3 - chance < ac + spell_bonus);
4628 staticfn void
4629 disintegrate_mon(
4630 struct monst *mon,
4631 int type, /* hero vs other */
4632 const char *fltxt)
4634 struct obj *otmp, *otmp2, *m_amulet = mlifesaver(mon);
4636 if (canseemon(mon)) {
4637 if (!m_amulet)
4638 pline("%s is disintegrated!", Monnam(mon));
4639 else
4640 hit(fltxt, mon, "!");
4643 /* note: worn amulet of life saving must be preserved in order to operate */
4644 #define oresist_disintegration(obj) \
4645 (objects[obj->otyp].oc_oprop == DISINT_RES || obj_resists(obj, 5, 50) \
4646 || is_quest_artifact(obj) || obj == m_amulet)
4648 for (otmp = mon->minvent; otmp; otmp = otmp2) {
4649 otmp2 = otmp->nobj;
4650 if (!oresist_disintegration(otmp)) {
4651 extract_from_minvent(mon, otmp, TRUE, TRUE);
4652 obfree(otmp, (struct obj *) 0);
4656 #undef oresist_disintegration
4658 if (type < 0)
4659 monkilled(mon, (char *) 0, -AD_RBRE);
4660 else
4661 xkilled(mon, XKILL_NOMSG | XKILL_NOCORPSE);
4664 void
4665 ubuzz(int type, int nd)
4667 dobuzz(type, nd, u.ux, u.uy, u.dx, u.dy, TRUE);
4670 void
4671 buzz(int type, int nd, coordxy sx, coordxy sy, int dx, int dy)
4673 dobuzz(type, nd, sx, sy, dx, dy, TRUE);
4677 * type == 0 to 9 : you zapping a wand
4678 * type == 10 to 19 : you casting a spell
4679 * type == 20 to 29 : you breathing as a monster
4680 * type == -10 to -19 : monster casting spell
4681 * type == -20 to -29 : monster breathing at you
4682 * type == -30 to -39 : monster zapping a wand
4683 * called with dx = dy = 0 for vertical bolts
4685 void
4686 dobuzz(
4687 int type, /* 0..29 (by hero) or -39..-10 (by monster) */
4688 int nd, /* damage strength ('number of dice') */
4689 coordxy sx, coordxy sy, /* starting point */
4690 int dx, int dy, /* direction delta */
4691 boolean say) /* announce out of sight hit/miss events if true */
4693 int range, fltyp = zaptype(type), damgtype = fltyp % 10;
4694 coordxy lsx, lsy;
4695 struct monst *mon;
4696 coord save_bhitpos;
4697 boolean shopdamage = FALSE,
4698 fireball = (type == ZT_SPELL(ZT_FIRE)), /* set once */
4699 gas_hit = FALSE; /* will be set during each iteration */
4700 struct obj *otmp;
4701 int spell_type;
4702 int hdmgtype = Hallucination ? rn2(6) : damgtype;
4704 /* if it's a Hero Spell then get its SPE_TYPE */
4705 spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + damgtype : 0;
4707 if (u.uswallow) {
4708 int tmp;
4710 if (type < 0)
4711 return;
4712 tmp = zhitm(u.ustuck, type, nd, &otmp);
4713 if (!u.ustuck) {
4714 u.uswallow = 0;
4715 } else {
4716 pline("%s rips into %s%s", The(flash_str(fltyp, FALSE)),
4717 mon_nam(u.ustuck), exclam(tmp));
4718 /* Using disintegration from the inside only makes a hole... */
4719 if (tmp == MAGIC_COOKIE)
4720 u.ustuck->mhp = 0;
4721 if (DEADMONSTER(u.ustuck))
4722 killed(u.ustuck);
4724 return;
4726 if (type < 0)
4727 newsym(u.ux, u.uy);
4728 range = rn1(7, 7);
4729 if (dx == 0 && dy == 0)
4730 range = 1;
4731 save_bhitpos = gb.bhitpos;
4733 tmp_at(DISP_BEAM, zapdir_to_glyph(dx, dy, hdmgtype));
4734 while (range-- > 0) {
4735 lsx = sx;
4736 sx += dx;
4737 lsy = sy;
4738 sy += dy;
4739 if (!isok(sx, sy) || levl[sx][sy].typ == STONE)
4740 goto make_bounce;
4742 mon = m_at(sx, sy);
4743 if (cansee(sx, sy)) {
4744 /* reveal/unreveal invisible monsters before tmp_at() */
4745 if (mon && !canspotmon(mon))
4746 map_invisible(sx, sy);
4747 else if (!mon)
4748 (void) unmap_invisible(sx, sy);
4749 if (ZAP_POS(levl[sx][sy].typ)
4750 || (isok(lsx, lsy) && cansee(lsx, lsy)))
4751 tmp_at(sx, sy);
4752 nh_delay_output(); /* wait a little */
4755 /* hit() and miss() need gb.bhitpos to match the target */
4756 gb.bhitpos.x = sx, gb.bhitpos.y = sy;
4757 gas_hit = (damgtype == ZT_POISON_GAS);
4758 /* fireballs only damage when they explode; poison gas leaves
4759 a trail of 1x1 clouds via zap_over_floor(), but that gets
4760 skipped for a hit that is reflected so is deferred until we
4761 know whether reflection is happening */
4762 if (!fireball && !gas_hit) {
4763 range += zap_over_floor(sx, sy, type, &shopdamage, TRUE, 0);
4764 /* zap with fire -> melt ice -> drown monster, so monster
4765 found and cached above might not be here any more */
4766 mon = m_at(sx, sy);
4769 if (mon) {
4770 if (fireball)
4771 break;
4772 if (type >= 0)
4773 mon->mstrategy &= ~STRAT_WAITMASK;
4774 buzzmonst:
4775 gn.notonhead = (mon->mx != gb.bhitpos.x
4776 || mon->my != gb.bhitpos.y);
4777 if (zap_hit(find_mac(mon), spell_type)) {
4778 if (mon_reflects(mon, (char *) 0)) {
4779 if (cansee(mon->mx, mon->my)) {
4780 hit(flash_str(fltyp, FALSE), mon, exclam(0));
4781 shieldeff(mon->mx, mon->my);
4782 (void) mon_reflects(mon,
4783 "But it reflects from %s %s!");
4784 gas_hit = FALSE;
4786 dx = -dx;
4787 dy = -dy;
4788 } else {
4789 boolean mon_could_move = mon->mcanmove;
4790 int tmp = zhitm(mon, type, nd, &otmp);
4792 if (is_rider(mon->data)
4793 && abs(type) == ZT_BREATH(ZT_DEATH)) {
4794 if (canseemon(mon)) {
4795 hit(flash_str(fltyp, FALSE), mon, ".");
4796 pline("%s disintegrates.", Monnam(mon));
4797 pline("%s body reintegrates before your %s!",
4798 s_suffix(Monnam(mon)),
4799 (eyecount(gy.youmonst.data) == 1)
4800 ? body_part(EYE)
4801 : makeplural(body_part(EYE)));
4802 pline("%s resurrects!", Monnam(mon));
4804 mon->mhp = mon->mhpmax;
4805 break; /* Out of while loop */
4807 if (mon->data == &mons[PM_DEATH] && damgtype == ZT_DEATH) {
4808 if (canseemon(mon)) {
4809 hit(flash_str(fltyp, FALSE), mon, ".");
4810 pline("%s absorbs the deadly %s!", Monnam(mon),
4811 type == ZT_BREATH(ZT_DEATH) ? "blast"
4812 : "ray");
4813 pline("It seems even stronger than before.");
4815 break; /* Out of while loop */
4818 if (tmp == MAGIC_COOKIE) { /* disintegration */
4819 disintegrate_mon(mon, type, flash_str(fltyp, FALSE));
4820 } else if (DEADMONSTER(mon)) {
4821 if (type < 0) {
4822 /* mon has just been killed by another monster */
4823 monkilled(mon, flash_str(fltyp, FALSE), AD_RBRE);
4824 } else {
4825 int xkflags = XKILL_GIVEMSG; /* killed(mon); */
4827 /* killed by hero; we know 'type' isn't negative;
4828 if it's fire, highly flammable monsters leave
4829 no corpse; don't bother reporting that they
4830 "burn completely" -- unnecessary verbosity */
4831 if (damgtype == ZT_FIRE
4832 /* paper golem or straw golem */
4833 && completelyburns(mon->data))
4834 xkflags |= XKILL_NOCORPSE;
4835 xkilled(mon, xkflags);
4837 } else {
4838 if (!otmp) {
4839 /* normal non-fatal hit */
4840 if (say || canseemon(mon))
4841 hit(flash_str(fltyp, FALSE), mon, exclam(tmp));
4842 } else {
4843 /* some armor was destroyed; no damage done */
4844 if (canseemon(mon))
4845 pline("%s %s is disintegrated!",
4846 s_suffix(Monnam(mon)),
4847 distant_name(otmp, xname));
4848 m_useup(mon, otmp);
4850 if (mon_could_move && !mon->mcanmove) /* ZT_SLEEP */
4851 slept_monst(mon);
4852 if (damgtype != ZT_SLEEP)
4853 wakeup(mon, (type >= 0) ? TRUE : FALSE);
4856 range -= 2;
4857 } else {
4858 if (say || canseemon(mon))
4859 miss(flash_str(fltyp, FALSE), mon);
4861 } else if (u_at(sx, sy) && range >= 0) {
4862 nomul(0);
4863 if (u.usteed && !rn2(3) && !mon_reflects(u.usteed, (char *) 0)) {
4864 mon = u.usteed;
4865 goto buzzmonst;
4866 } else if (zap_hit((int) u.uac, 0)) {
4867 range -= 2;
4868 pline_dir(xytod(-dx, -dy), "%s hits you!",
4869 The(flash_str(fltyp, FALSE)));
4870 if (Reflecting) {
4871 if (!Blind) {
4872 (void) ureflects("But %s reflects from your %s!",
4873 "it");
4874 } else
4875 pline("For some reason you are not affected.");
4876 monstseesu(M_SEEN_REFL);
4877 dx = -dx;
4878 dy = -dy;
4879 shieldeff(sx, sy);
4880 gas_hit = FALSE;
4881 } else {
4882 /* flash_str here only used for killer; suppress
4883 * hallucination */
4884 zhitu(type, nd, flash_str(fltyp, TRUE), sx, sy);
4885 monstunseesu(M_SEEN_REFL);
4887 } else if (!Blind) {
4888 pline("%s whizzes by you!", The(flash_str(fltyp, FALSE)));
4889 } else if (damgtype == ZT_LIGHTNING) {
4890 Your("%s tingles.", body_part(ARM));
4892 if (damgtype == ZT_LIGHTNING)
4893 (void) flashburn((long) d(nd, 50), TRUE);
4894 stop_occupation();
4895 nomul(0);
4897 /* gas that missed or that hit without being reflected will leave
4898 a 1x1 cloud here; the earlier zap_over_floor() was deferred */
4899 if (gas_hit)
4900 (void) zap_over_floor(sx, sy, type, &shopdamage, TRUE, 0);
4902 if (!ZAP_POS(levl[sx][sy].typ)
4903 || (closed_door(sx, sy) && range >= 0)) {
4904 int bounce, bchance;
4905 uchar rmn;
4907 make_bounce:
4908 bchance = (!isok(sx, sy) || levl[sx][sy].typ == STONE) ? 10
4909 : (In_mines(&u.uz) && IS_WALL(levl[sx][sy].typ)) ? 20
4910 : 75;
4911 bounce = 0;
4912 if ((--range > 0 && isok(lsx, lsy) && cansee(lsx, lsy))
4913 || fireball) {
4914 if (Is_airlevel(&u.uz)) { /* nothing to bounce off of */
4915 pline_The("%s vanishes into the aether!",
4916 flash_str(fltyp, FALSE));
4917 if (fireball)
4918 type = ZT_WAND(ZT_FIRE); /* skip pending fireball */
4919 break;
4920 } else if (fireball) {
4921 sx = lsx;
4922 sy = lsy;
4923 break; /* fireballs explode before the obstacle */
4924 } else
4925 pline_The("%s bounces!", flash_str(fltyp, FALSE));
4927 if (!dx || !dy || !rn2(bchance)) {
4928 dx = -dx;
4929 dy = -dy;
4930 } else {
4931 if (isok(sx, lsy) && ZAP_POS(rmn = levl[sx][lsy].typ)
4932 && !closed_door(sx, lsy)
4933 && (IS_ROOM(rmn) || (isok(sx + dx, lsy)
4934 && ZAP_POS(levl[sx + dx][lsy].typ))))
4935 bounce = 1;
4936 if (isok(lsx, sy) && ZAP_POS(rmn = levl[lsx][sy].typ)
4937 && !closed_door(lsx, sy)
4938 && (IS_ROOM(rmn) || (isok(lsx, sy + dy)
4939 && ZAP_POS(levl[lsx][sy + dy].typ))))
4940 if (!bounce || rn2(2))
4941 bounce = 2;
4943 switch (bounce) {
4944 case 0:
4945 dx = -dx;
4946 FALLTHROUGH;
4947 /*FALLTHRU*/
4948 case 1:
4949 dy = -dy;
4950 break;
4951 case 2:
4952 dx = -dx;
4953 break;
4955 tmp_at(DISP_CHANGE, zapdir_to_glyph(dx, dy, hdmgtype));
4959 tmp_at(DISP_END, 0);
4960 if (fireball)
4961 explode(sx, sy, type, d(12, 6), 0, EXPL_FIERY);
4962 if (shopdamage)
4963 pay_for_damage(damgtype == ZT_FIRE ? "burn away"
4964 : damgtype == ZT_COLD ? "shatter"
4965 /* "damage" indicates wall rather than door */
4966 : damgtype == ZT_ACID ? "damage"
4967 : damgtype == ZT_DEATH ? "disintegrate"
4968 : "destroy",
4969 FALSE);
4970 gb.bhitpos = save_bhitpos;
4973 void
4974 melt_ice(coordxy x, coordxy y, const char *msg)
4976 struct rm *lev = &levl[x][y];
4977 struct obj *otmp;
4978 struct monst *mtmp;
4980 if (!msg)
4981 msg = "The ice crackles and melts.";
4982 if (lev->typ == DRAWBRIDGE_UP || lev->typ == DRAWBRIDGE_DOWN) {
4983 lev->drawbridgemask &= ~DB_ICE; /* revert to DB_MOAT */
4984 } else { /* lev->typ == ICE */
4985 lev->typ = (lev->icedpool == ICED_POOL ? POOL : MOAT);
4986 lev->icedpool = 0;
4988 spot_stop_timers(x, y, MELT_ICE_AWAY); /* no more ice to melt away */
4989 if (t_at(x, y))
4990 trap_ice_effects(x, y, TRUE); /* TRUE because ice_is_melting */
4991 obj_ice_effects(x, y, FALSE);
4992 unearth_objs(x, y);
4993 if (Underwater)
4994 vision_recalc(1);
4995 newsym(x, y);
4996 if (cansee(x, y) || u_at(x, y))
4997 Norep("%s", msg);
4998 if ((otmp = sobj_at(BOULDER, x, y)) != 0) {
4999 if (cansee(x, y))
5000 pline("%s settles...", An(xname(otmp)));
5001 do {
5002 obj_extract_self(otmp); /* boulder isn't being pushed */
5003 if (!boulder_hits_pool(otmp, x, y, FALSE))
5004 impossible("melt_ice: no pool?");
5005 /* try again if there's another boulder and pool didn't fill */
5006 } while (is_pool(x, y) && (otmp = sobj_at(BOULDER, x, y)) != 0);
5007 newsym(x, y);
5009 if (u_at(x, y))
5010 spoteffects(TRUE); /* possibly drown, notice objects */
5011 else if (is_pool(x, y) && (mtmp = m_at(x, y)) != 0)
5012 (void) minliquid(mtmp);
5015 #define MIN_ICE_TIME 50
5016 #define MAX_ICE_TIME 2000
5018 * Usually start a melt_ice timer; sometimes the ice will become
5019 * permanent instead.
5021 void
5022 start_melt_ice_timeout(
5023 coordxy x, coordxy y,
5024 long min_time) /* <x,y>'s old melt timeout (deleted by time we get here) */
5026 int when;
5027 long where;
5029 when = (int) min_time;
5030 if (when < MIN_ICE_TIME - 1)
5031 when = MIN_ICE_TIME - 1;
5033 /* random timeout; surrounding ice locations ought to be a factor... */
5034 while (++when <= MAX_ICE_TIME)
5035 if (!rn2((MAX_ICE_TIME - when) + MIN_ICE_TIME))
5036 break;
5038 /* if we're within MAX_ICE_TIME, install a melt timer;
5039 otherwise, omit it to leave this ice permanent */
5040 if (when <= MAX_ICE_TIME) {
5041 where = ((long) x << 16) | (long) y;
5042 (void) start_timer((long) when, TIMER_LEVEL, MELT_ICE_AWAY,
5043 long_to_any(where));
5046 #undef MIN_ICE_TIME
5047 #undef MAX_ICE_TIME
5050 * Called when ice has melted completely away.
5052 void
5053 melt_ice_away(anything *arg, long timeout UNUSED)
5055 coordxy x, y;
5056 long where = arg->a_long;
5057 boolean save_mon_moving = svc.context.mon_moving; /* will be False */
5059 /* melt_ice -> minliquid -> mondead|xkilled shouldn't credit/blame hero */
5060 svc.context.mon_moving = TRUE; /* hero isn't causing this ice to melt */
5061 y = (coordxy) (where & 0xFFFF);
5062 x = (coordxy) ((where >> 16) & 0xFFFF);
5063 /* melt_ice does newsym when appropriate */
5064 melt_ice(x, y, "Some ice melts away.");
5065 svc.context.mon_moving = save_mon_moving;
5068 /* Burn floor scrolls, evaporate pools, etc... in a single square.
5069 * Used both for normal bolts of fire, cold, etc... and for fireballs.
5070 * Sets shopdamage to TRUE if a shop door is destroyed, and returns the
5071 * amount by which range is reduced (value is negative and will be added
5072 * to remaining range by caller; ignored by fireballs and poison gas).
5075 zap_over_floor(
5076 coordxy x, coordxy y, /* location */
5077 int type, /* damage type plus {wand|spell|breath} info */
5078 boolean *shopdamage, /* extra output if shop door is destroyed */
5079 boolean ignoremon, /* ignore any monster here */
5080 short exploding_wand_typ) /* supplied when breaking a wand; or POT_OIL
5081 * when a lit potion of oil explodes */
5083 const char *zapverb;
5084 struct monst *mon;
5085 struct trap *t;
5086 struct rm *lev = &levl[x][y];
5087 boolean see_it = cansee(x, y), yourzap;
5088 int rangemod = 0, damgtype = zaptype(type) % 10;
5089 boolean lavawall = (lev->typ == LAVAWALL);
5091 if (type == PHYS_EXPL_TYPE) {
5092 /* this won't have any effect on the floor */
5093 return -1000; /* not a zap anyway, shouldn't matter */
5096 switch (damgtype) {
5097 case ZT_FIRE:
5098 t = t_at(x, y);
5099 if (t && t->ttyp == WEB) {
5100 /* a burning web is too flimsy to notice if you can't see it */
5101 if (see_it)
5102 Norep("A web bursts into flames!");
5103 (void) delfloortrap(t), t = (struct trap *) 0;
5104 if (see_it)
5105 newsym(x, y);
5107 if (is_ice(x, y)) {
5108 melt_ice(x, y, (char *) 0);
5109 } else if (is_pool(x, y)) {
5110 boolean on_water_level = Is_waterlevel(&u.uz), msggiven = FALSE;
5111 const char *msgtxt = (!Deaf)
5112 ? "You hear hissing gas." /* Deaf-aware */
5113 : (type >= 0)
5114 ? "That seemed remarkably uneventful."
5115 : (char *) 0;
5117 /* don't create steam clouds on Plane of Water; air bubble
5118 movement and gas regions don't understand each other */
5119 if (!on_water_level) {
5120 create_gas_cloud(x, y, rnd(5), 0); /* 1..5, no damg */
5121 if (iflags.last_msg == PLNMSG_ENVELOPED_IN_GAS)
5122 msggiven = TRUE;
5125 if (lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP or WATER */
5126 t = (struct trap *) 0;
5127 if (on_water_level)
5128 msgtxt = (see_it || !Deaf) ? "Some water boils." : 0;
5129 else if (see_it)
5130 msgtxt = "Some water evaporates.";
5131 } else {
5132 rangemod -= 3;
5133 lev->typ = ROOM, lev->flags = 0;
5134 t = maketrap(x, y, PIT);
5135 /*if (t) -- this was before the vapor cloud was added --
5136 t->tseen = 1;*/
5137 if (see_it)
5138 msgtxt = "The water evaporates.";
5140 if (msgtxt && !msggiven)
5141 Norep("%s", msgtxt);
5143 if (lev->typ == ROOM) { /* POOL changed to ROOM above */
5144 if ((mon = m_at(x, y)) != 0) {
5145 /* probably ought to do some hefty damage to any
5146 creature caught in boiling water;
5147 at a minimum, eels are forced out of hiding */
5148 if (is_swimmer(mon->data) && mon->mundetected) {
5149 mon->mundetected = 0;
5152 newsym(x, y);
5153 if (t) {
5154 /* if water walking/swimming/magical breathing, maybe fall
5155 into the new pit (after the water evaporation message);
5156 if flying or levitating, nothing will happen */
5157 if (u_at(x, y))
5158 dotrap(t, NO_TRAP_FLAGS);
5159 else if (mon)
5160 mintrap(mon, NO_TRAP_FLAGS);
5163 } else if (IS_FOUNTAIN(lev->typ)) {
5164 create_gas_cloud(x, y, rnd(3), 0); /* 1..3, no damage */
5165 if (see_it)
5166 pline("Steam billows from the fountain.");
5167 rangemod -= 1;
5168 dryup(x, y, type > 0);
5170 break; /* ZT_FIRE */
5172 case ZT_COLD:
5173 if (is_pool(x, y) || is_lava(x, y) || lavawall) {
5174 boolean lava = (is_lava(x, y) || lavawall),
5175 moat = is_moat(x, y);
5176 int chance = max(2, 5 + svl.level.flags.temperature * 10);
5178 if (IS_WATERWALL(lev->typ) || (lavawall && rn2(chance))) {
5179 /* For now, don't let WATER freeze. */
5180 Soundeffect(se_soft_crackling, 100);
5181 if (see_it)
5182 pline_The("%s freezes for a moment.",
5183 hliquid(lavawall ? "lava" : "water"));
5184 else
5185 You_hear("a soft crackling.");
5186 rangemod -= 1000; /* stop */
5187 } else {
5188 char buf[BUFSZ];
5190 Strcpy(buf, waterbody_name(x, y)); /* for MOAT */
5191 rangemod -= 3;
5192 if (lev->typ == DRAWBRIDGE_UP) {
5193 lev->drawbridgemask &= ~DB_UNDER; /* clear lava */
5194 lev->drawbridgemask |= (lava ? DB_FLOOR : DB_ICE);
5195 } else {
5196 lev->icedpool = lava ? 0
5197 : (lev->typ == POOL) ? ICED_POOL
5198 : ICED_MOAT;
5199 if (lavawall) {
5200 if ((isok(x, y-1) && IS_WALL(levl[x][y-1].typ))
5201 || (isok(x, y+1) && IS_WALL(levl[x][y+1].typ)))
5202 lev->typ = VWALL;
5203 else
5204 lev->typ = HWALL;
5205 fix_wall_spines(max(0,x-1), max(0,y-1),
5206 min(COLNO-1,x+1), min(ROWNO-1,y+1));
5207 } else {
5208 lev->typ = lava ? ROOM : ICE;
5211 bury_objs(x, y);
5212 if (!lava) {
5213 Soundeffect(se_soft_crackling, 30);
5215 if (see_it) {
5216 if (lava)
5217 Norep("The %s cools and solidifies.",
5218 hliquid("lava"));
5219 else if (moat)
5220 Norep("The %s is bridged with ice!", buf);
5221 else
5222 Norep("The %s freezes.", hliquid("water"));
5223 newsym(x, y);
5224 } else if (!lava) {
5225 You_hear("a crackling sound.");
5227 if (u_at(x, y)) {
5228 if (u.uinwater) { /* not just `if (Underwater)' */
5229 /* leave the no longer existent water */
5230 set_uinwater(0); /* u.uinwater = 0 */
5231 u.uundetected = 0;
5232 docrt();
5233 gv.vision_full_recalc = 1;
5234 } else if (u.utrap && u.utraptype == TT_LAVA) {
5235 if (Passes_walls) {
5236 You("pass through the now-solid rock.");
5237 reset_utrap(TRUE);
5238 } else {
5239 set_utrap(rn1(50, 20), TT_INFLOOR);
5240 You("are firmly stuck in the cooling rock.");
5243 } else if ((mon = m_at(x, y)) != 0) {
5244 /* probably ought to do some hefty damage to any
5245 non-ice creature caught in freezing water;
5246 at a minimum, eels are forced out of hiding */
5247 if (is_swimmer(mon->data) && mon->mundetected) {
5248 mon->mundetected = 0;
5249 newsym(x, y);
5252 if (!lava) {
5253 start_melt_ice_timeout(x, y, 0L);
5254 obj_ice_effects(x, y, TRUE);
5256 } /* ?WATER */
5258 } else if (is_ice(x, y)) {
5259 long melt_time;
5261 /* Already ice here, so just firm it up. */
5262 /* Now ensure that only ice that is already timed is affected */
5263 if ((melt_time = spot_time_left(x, y, MELT_ICE_AWAY)) != 0L) {
5264 spot_stop_timers(x, y, MELT_ICE_AWAY);
5265 start_melt_ice_timeout(x, y, melt_time);
5268 break; /* ZT_COLD */
5270 case ZT_POISON_GAS:
5271 /* poison gas with range 1: green dragon/iron golem breath (AD_DRST);
5272 caller is placing a series of 1x1 clouds along the zap's path;
5273 <x,y> for wall locations might be included--reject those */
5274 if (ZAP_POS(lev->typ))
5275 (void) create_gas_cloud(x, y, 1, 8);
5276 break;
5278 case ZT_LIGHTNING:
5279 FALLTHROUGH;
5280 /*FALLTHRU*/
5281 case ZT_ACID:
5282 if (lev->typ == IRONBARS) {
5283 if (damgtype == ZT_LIGHTNING && rn2(10))
5284 break;
5285 if ((lev->wall_info & W_NONDIGGABLE) != 0) {
5286 if (see_it)
5287 Norep("The %s %s somewhat but remain intact.",
5288 defsyms[S_bars].explanation,
5289 (damgtype == ZT_ACID) ? "corrode" : "melt");
5290 /* but nothing actually happens... */
5291 } else {
5292 rangemod -= 3;
5293 if (see_it)
5294 Norep("The %s %s.", defsyms[S_bars].explanation,
5295 (damgtype == ZT_ACID) ? "corrode away" : "melt");
5296 dissolve_bars(x, y);
5297 if (*in_rooms(x, y, SHOPBASE)) {
5298 add_damage(x, y, (type >= 0) ? SHOP_BARS_COST : 0L);
5299 if (type >= 0)
5300 *shopdamage = TRUE;
5304 break; /* ZT_ACID */
5306 default:
5307 break;
5310 /* set up zap text for possible door feedback; for exploding wand, we
5311 want "the blast" rather than "your blast" even if hero caused it */
5312 yourzap = (type >= 0 && !exploding_wand_typ);
5313 zapverb = "blast"; /* breath attack or wand explosion */
5314 if (!exploding_wand_typ) {
5315 int ztype = zaptype(type); /* 0..29 for both hero and monsters */
5317 if (ztype < ZT_SPELL(0))
5318 zapverb = "bolt"; /* wand zap */
5319 else if (ztype < ZT_BREATH(0))
5320 zapverb = "spell";
5321 } else if (exploding_wand_typ == POT_OIL
5322 || exploding_wand_typ == SCR_FIRE) {
5323 /* breakobj() -> explode_oil() -> splatter_burning_oil()
5324 -> explode(ZT_SPELL(ZT_FIRE), BURNING_OIL)
5325 -> zap_over_floor(ZT_SPELL(ZT_FIRE), POT_OIL) */
5326 /* leave zapverb as "blast"; exploding_wand_typ was nonzero, so
5327 'yourzap' is FALSE and the result will be "the blast" */
5328 exploding_wand_typ = 0; /* not actually an exploding wand */
5331 /* secret door gets revealed, converted into regular door */
5332 if (levl[x][y].typ == SDOOR) {
5333 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
5334 recalc_block_point(x, y);
5335 /* target spot will now pass closed_door() test below
5336 (except on rogue level) */
5337 newsym(x, y);
5338 if (see_it)
5339 pline("%s %s reveals a secret door.",
5340 yourzap ? "Your" : "The", zapverb);
5341 else if (Is_rogue_level(&u.uz))
5342 draft_message(FALSE); /* "You feel a draft." (open doorway) */
5345 /* regular door absorbs remaining zap range, possibly gets destroyed */
5346 if (closed_door(x, y)) {
5347 int new_doormask = -1;
5348 const char *see_txt = 0, *sense_txt = 0, *hear_txt = 0;
5350 rangemod = -1000;
5351 switch (damgtype) {
5352 case ZT_FIRE:
5353 new_doormask = D_NODOOR;
5354 see_txt = "The door is consumed in flames!";
5355 sense_txt = "smell smoke.";
5356 break;
5357 case ZT_COLD:
5358 new_doormask = D_NODOOR;
5359 see_txt = "The door freezes and shatters!";
5360 hear_txt = "a deep cracking sound.";
5361 break;
5362 case ZT_DEATH:
5363 /* death spells/wands don't disintegrate */
5364 if (abs(type) != ZT_BREATH(ZT_DEATH))
5365 goto def_case;
5366 new_doormask = D_NODOOR;
5367 see_txt = "The door disintegrates!";
5368 hear_txt = "crashing wood.";
5369 break;
5370 case ZT_LIGHTNING:
5371 new_doormask = D_BROKEN;
5372 see_txt = "The door splinters!";
5373 hear_txt = "crackling.";
5374 break;
5375 default:
5376 def_case:
5377 if (exploding_wand_typ > 0) {
5378 /* Magical explosion from misc exploding wand */
5379 if (exploding_wand_typ == WAN_STRIKING) {
5380 new_doormask = D_BROKEN;
5381 see_txt = "The door crashes open!";
5382 sense_txt = "feel a burst of cool air.";
5383 break;
5386 if (see_it) {
5387 /* "the door absorbs the blast" would be
5388 inaccurate for an exploding wand since
5389 other adjacent locations still get hit */
5390 if (exploding_wand_typ)
5391 pline_The("door remains intact.");
5392 else
5393 pline_The("door absorbs %s %s!", yourzap ? "your" : "the",
5394 zapverb);
5395 } else
5396 You_feel("vibrations.");
5397 break;
5399 if (new_doormask >= 0) { /* door gets broken */
5400 if (*in_rooms(x, y, SHOPBASE)) {
5401 if (type >= 0) {
5402 add_damage(x, y, SHOP_DOOR_COST);
5403 *shopdamage = TRUE;
5404 } else /* caused by monster */
5405 add_damage(x, y, 0L);
5407 lev->doormask = new_doormask;
5408 recalc_block_point(x, y); /* vision */
5409 if (see_it) {
5410 pline1(see_txt);
5411 newsym(x, y);
5412 } else if (sense_txt) {
5413 You1(sense_txt);
5414 } else if (hear_txt)
5415 You_hear1(hear_txt);
5416 if (picking_at(x, y)) {
5417 stop_occupation();
5418 reset_pick();
5423 if (OBJ_AT(x, y) && damgtype == ZT_FIRE)
5424 if (burn_floor_objects(x, y, FALSE, type > 0) && couldsee(x, y)) {
5425 newsym(x, y);
5426 You("%s of smoke.", !Blind ? "see a puff" : "smell a whiff");
5428 if (!ignoremon && (mon = m_at(x, y)) != 0)
5429 wakeup(mon, (type >= 0) ? TRUE : FALSE);
5430 return rangemod;
5433 /* monster has cast flames or frost at target on <x,y>; called by mcastu() */
5434 void
5435 mon_spell_hits_spot(
5436 struct monst *caster UNUSED,
5437 int adtyp, /* canonical damage type */
5438 coordxy x, coordxy y) /* so far, only used for targeting <u.ux,u.uy> */
5440 /* "shower of missiles" or [hypothetical] "acid rain" attack:
5441 thoroughly clobber an engraving (unless its type makes it be
5442 scuff-protected); zap_over_floor() doesn't handle this */
5443 if (adtyp == AD_MAGM || adtyp == AD_ACID) {
5444 struct engr *ep = engr_at(x, y);
5445 char *etext = ep ? ep->engr_txt[actual_text] : NULL;
5447 if (etext)
5448 wipe_engr_at(x, y, (int) strlen(etext) + d(6, 6), TRUE);
5449 /* hero and player will still remember prior text until the spot
5450 is re-examined (lookhere or move off and back on) */
5453 /* hit items and/or terrain; only matters for AD_FIRE and AD_COLD but
5454 accept any basic damage type that zap_over_floor() might handle */
5455 if (adtyp >= AD_MAGM && adtyp <= AD_ACID) {
5456 boolean shopdummy = FALSE; /* zap_over_floor() requires this even
5457 * though it's only used when zapdmgtyp
5458 * is non-negative (hero's fault) */
5459 int zt_typ = adtyp - 1, /* convert AD_xxxx to ZT_xxxx */
5460 zapdmgtyp = -ZT_SPELL(zt_typ); /* damage is from monster spell */
5462 (void) zap_over_floor(x, y, zapdmgtyp, &shopdummy, TRUE, 0);
5463 } else {
5464 impossible("Unsupported damage type (%d) for mon_spell_hits_spot.",
5465 adtyp);
5469 /* fractured by pick-axe or wand of striking or by vault guard or shopkeeper */
5470 void
5471 fracture_rock(struct obj *obj) /* no texts here! */
5473 coordxy x, y;
5474 boolean by_you = !svc.context.mon_moving;
5476 if (by_you && get_obj_location(obj, &x, &y, 0) && costly_spot(x, y)) {
5477 struct monst *shkp = 0;
5478 char objroom = *in_rooms(x, y, SHOPBASE);
5480 if (billable(&shkp, obj, objroom, FALSE)) {
5481 /* shop message says "you owe <shk> <$> for it!" so we need
5482 to precede that with a message explaining what "it" is */
5483 You("fracture %s %s.", s_suffix(shkname(shkp)), xname(obj));
5484 /* breakobj won't destroy fracturing statue or boulder but
5485 will charge for shop goods */
5486 (void) breakobj(obj, x, y, TRUE, FALSE);
5489 if (by_you && obj->otyp == BOULDER)
5490 sokoban_guilt();
5492 obj->otyp = ROCK;
5493 obj->oclass = GEM_CLASS;
5494 obj->quan = (long) rn1(60, 7);
5495 obj->owt = weight(obj);
5496 obj->dknown = obj->bknown = obj->rknown = 0;
5497 obj->known = objects[obj->otyp].oc_uses_known ? 0 : 1;
5498 dealloc_oextra(obj);
5500 if (obj->where == OBJ_FLOOR) {
5501 obj_extract_self(obj); /* move rocks back on top */
5502 place_object(obj, obj->ox, obj->oy);
5503 if (!does_block(obj->ox, obj->oy, &levl[obj->ox][obj->oy])) {
5504 unblock_point(obj->ox, obj->oy);
5505 /* need immediate update in case this is a striking/force bolt
5506 zap that is about hit more things */
5507 vision_recalc(0);
5509 if (cansee(obj->ox, obj->oy))
5510 newsym(obj->ox, obj->oy);
5514 /* handle statue hit by striking/force bolt/pick-axe */
5515 boolean
5516 break_statue(struct obj *obj)
5518 /* [obj is assumed to be on floor, so no get_obj_location() needed] */
5519 struct trap *trap = t_at(obj->ox, obj->oy);
5520 struct obj *item;
5521 boolean by_you = !svc.context.mon_moving;
5523 if (trap && trap->ttyp == STATUE_TRAP
5524 && activate_statue_trap(trap, obj->ox, obj->oy, TRUE))
5525 return FALSE;
5526 /* drop any objects contained inside the statue */
5527 while ((item = obj->cobj) != 0) {
5528 obj_extract_self(item);
5529 place_object(item, obj->ox, obj->oy);
5531 if (by_you && Role_if(PM_ARCHEOLOGIST)
5532 && (obj->spe & CORPSTAT_HISTORIC)) {
5533 You_feel("guilty about damaging such a historic statue.");
5534 adjalign(-1);
5536 obj->spe = 0;
5537 fracture_rock(obj);
5538 return TRUE;
5541 /* Return TRUE if obj is eligible to pass to maybe_destroy_item given
5542 * the type of elemental damage it's being subjected to.
5543 * Note that things like the Book of the Dead are eligible even though they
5544 * won't get destroyed, because it will attempt to be destroyed but print a
5545 * special message instead. */
5546 staticfn boolean
5547 destroyable(struct obj *obj, int adtyp)
5549 if (obj->oartifact) {
5550 /* don't destroy artifacts */
5551 return FALSE;
5553 if (obj->in_use && obj->quan == 1L) {
5554 /* not available for destroying */
5555 return FALSE;
5557 if (adtyp == AD_FIRE) {
5558 /* fire-magic items are immune */
5559 if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL) {
5560 return FALSE;
5562 if (obj->otyp == GLOB_OF_GREEN_SLIME || obj->oclass == POTION_CLASS
5563 || obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) {
5564 return TRUE;
5566 } else if (adtyp == AD_COLD) {
5567 /* non-water potions don't freeze and shatter */
5568 if (obj->oclass == POTION_CLASS && obj->otyp != POT_OIL) {
5569 return TRUE;
5571 } else if (adtyp == AD_ELEC) {
5572 if (obj->oclass != RING_CLASS && obj->oclass != WAND_CLASS) {
5573 return FALSE;
5575 /* electric-magic items are immune */
5576 if (obj->otyp != RIN_SHOCK_RESISTANCE && obj->otyp != WAN_LIGHTNING) {
5577 return TRUE;
5579 /* There used to be a commented out bit of code that would exclude
5580 * gc.current_wand, but it wasn't used, so it wasn't moved into this
5581 * function. */
5583 return FALSE;
5586 /* convert attack damage AD_foo to property resistance */
5587 staticfn int
5588 adtyp_to_prop(int dmgtyp)
5590 switch (dmgtyp) {
5591 case AD_COLD:
5592 return COLD_RES;
5593 case AD_FIRE:
5594 return FIRE_RES;
5595 case AD_ELEC:
5596 return SHOCK_RES;
5597 case AD_ACID:
5598 return ACID_RES;
5599 case AD_DISN:
5600 return DISINT_RES;
5601 default:
5602 break;
5604 return 0; /* prop_types start at 1 */
5607 /* Is hero wearing or wielding an object with resistance to attack
5608 damage type? Returns the percentage protection that the object gives. */
5610 u_adtyp_resistance_obj(int dmgtyp)
5612 int prop = adtyp_to_prop(dmgtyp);
5614 if (!prop)
5615 return 0;
5617 /* Items that give an extrinsic resistance give 99% protection to
5618 your items */
5619 if ((u.uprops[prop].extrinsic & (W_ARMOR | W_ACCESSORY | W_WEP)) != 0)
5620 return 99;
5622 /* Dwarvish cloaks give a 90% protection to items against heat and cold */
5623 if (uarmc && uarmc->otyp == DWARVISH_CLOAK
5624 && (dmgtyp == AD_COLD || dmgtyp == AD_FIRE))
5625 return 90;
5627 return 0;
5630 /* Rolls to see whether an object in inventory resists damage from the
5631 given damage type, due to an equipped item protecting it. Use
5632 u_adtyp_resistance_obj to discover whether objects are protected in
5633 general (e.g. for enlightenment) and this function to actually do
5634 the roll to see whether a specific object is protected.
5636 This function doesn't check for other reasons why an object might
5637 be protected; you will usually need to do an obj_resists() call
5638 too. */
5639 boolean
5640 inventory_resistance_check(int dmgtyp)
5642 int prob = u_adtyp_resistance_obj(dmgtyp);
5644 if (!prob)
5645 return FALSE;
5647 return rn2(100) < prob;
5650 /* for enlightenment; currently only useful in wizard mode; cf from_what() */
5651 char *
5652 item_what(int dmgtyp)
5654 static char whatbuf[50];
5655 const char *what = 0;
5656 int prop = adtyp_to_prop(dmgtyp);
5657 long xtrinsic = u.uprops[prop].extrinsic;
5659 whatbuf[0] = '\0';
5660 if (wizard) {
5661 if (!prop || !xtrinsic) {
5662 ; /* 'what' stays Null */
5663 } else if (xtrinsic & W_ARMC) {
5664 what = cloak_simple_name(uarmc);
5665 } else if (xtrinsic & W_ARM) {
5666 what = suit_simple_name(uarm); /* "dragon {scales,mail}" */
5667 } else if (xtrinsic & W_ARMU) {
5668 what = shirt_simple_name(uarmu);
5669 } else if (xtrinsic & W_ARMH) {
5670 what = helm_simple_name(uarmh);
5671 } else if (xtrinsic & W_ARMG) {
5672 what = gloves_simple_name(uarmg);
5673 } else if (xtrinsic & W_ARMF) {
5674 what = boots_simple_name(uarmf);
5675 } else if (xtrinsic & W_ARMS) {
5676 what = shield_simple_name(uarms);
5677 } else if (xtrinsic & (W_AMUL | W_TOOL)) {
5678 what = simpleonames((xtrinsic & W_AMUL) ? uamul : ublindf);
5679 } else if (xtrinsic & W_RING) {
5680 if ((xtrinsic & W_RING) == W_RING) /* both */
5681 what = "rings";
5682 else
5683 what = simpleonames((xtrinsic & W_RINGL) ? uleft : uright);
5684 } else if (xtrinsic & W_WEP) {
5685 what = simpleonames(uwep);
5687 /* format the output to be ready for enl_msg() to append it to
5688 "Your items {are,were} protected against <damage-type>" */
5689 if (what) /* strlen(what) will be less than 30 */
5690 Sprintf(whatbuf, " by your %.40s", what);
5692 return whatbuf;
5696 * destroy_strings[dindx][0:singular,1:plural,2:killer_reason]
5697 * [0] freezing potion
5698 * [1] boiling potion other than oil
5699 * [2] boiling potion of oil
5700 * [3] burning scroll
5701 * [4] burning spellbook
5702 * [5] shocked ring
5703 * [6] shocked wand
5704 * (books, rings, and wands don't stack so don't need plural form;
5705 * crumbling ring doesn't do damage so doesn't need killer reason)
5706 * externally referenced from trap.c.
5708 const char *const destroy_strings[][3] = {
5709 /* also used in trap.c */
5710 { "freezes and shatters", "freeze and shatter", "shattered potion" },
5711 { "boils and explodes", "boil and explode", "boiling potion" },
5712 { "ignites and explodes", "ignite and explode", "exploding potion" },
5713 { "catches fire and burns", "catch fire and burn", "burning scroll" },
5714 { "catches fire and burns", "", "burning book" },
5715 { "turns to dust and vanishes", "", "" },
5716 { "breaks apart and explodes", "", "exploding wand" },
5719 /* guts of destroy_items();
5720 caller must decide whether obj is eligible, though there's one case (Book
5721 of the Dead) in which an eligible item shouldn't be destroyed (it prints a
5722 special message instead).
5723 Returns the amount of damage done, but it's used differently depending on
5724 whether it's the player or a monster having an item destroyed: players lose
5725 the HP and possibly die in this function, and the return value is unused,
5726 whereas monsters return the damage to their caller to be taken off later */
5727 staticfn int
5728 maybe_destroy_item(
5729 struct monst *carrier,
5730 struct obj *obj,
5731 int dmgtyp)
5733 long i, cnt, quan;
5734 int dmg, xresist, skip, dindx;
5735 const char *mult;
5736 boolean u_carry = (carrier == &gy.youmonst);
5737 boolean vis = !u_carry && canseemon(carrier);
5738 boolean chargeit = FALSE;
5740 xresist = skip = 0;
5741 /* lint suppression */
5742 dmg = dindx = 0;
5743 quan = 0L;
5745 /* external worn item protects inventory? */
5746 if (u_carry && inventory_resistance_check(dmgtyp))
5747 return 0;
5749 switch (dmgtyp) {
5750 case AD_COLD:
5751 quan = obj->quan;
5752 dindx = 0;
5753 dmg = rnd(4);
5754 break;
5755 case AD_FIRE:
5756 xresist = (obj->oclass != POTION_CLASS
5757 && obj->otyp != GLOB_OF_GREEN_SLIME
5758 && (u_carry ? Fire_resistance : resists_fire(carrier)));
5759 if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
5760 skip = 1;
5761 if (u_carry ? !Blind : vis) {
5762 pline("%s glows a strange %s, but remains intact.",
5763 The(u_carry ? xname(obj) : distant_name(obj, xname)),
5764 hcolor("dark red"));
5766 break;
5768 quan = obj->quan;
5769 switch (obj->oclass) {
5770 case POTION_CLASS:
5771 dindx = (obj->otyp != POT_OIL) ? 1 : 2;
5772 dmg = rnd(6);
5773 break;
5774 case SCROLL_CLASS:
5775 dindx = 3;
5776 dmg = 1;
5777 break;
5778 case SPBOOK_CLASS:
5779 dindx = 4;
5780 dmg = 1;
5781 break;
5782 case FOOD_CLASS: /* only GLOB_OF_GREEN_SLIME */
5783 dindx = 1; /* boil and explode */
5784 dmg = (obj->owt + 19) / 20;
5785 break;
5787 break;
5788 case AD_ELEC:
5789 xresist = (obj->oclass != RING_CLASS
5790 && (u_carry ? Shock_resistance : resists_elec(carrier)));
5791 quan = obj->quan;
5792 switch (obj->oclass) {
5793 case RING_CLASS:
5794 if (((obj->owornmask & W_RING) && uarmg && !is_metallic(uarmg))
5795 || obj->otyp == RIN_SHOCK_RESISTANCE) {
5796 skip++;
5797 break;
5798 } else if (objects[obj->otyp].oc_charged && rn2(3)) {
5799 chargeit = TRUE;
5800 break;
5802 dindx = 5;
5803 dmg = 0;
5804 break;
5805 case WAND_CLASS:
5806 dindx = 6;
5807 dmg = rnd(10);
5808 break;
5810 break;
5811 default:
5812 skip = 1; /* just in case ineligible damage type gets through... */
5813 impossible("maybe_destroy_item with unexpected dmgtyp %d", dmgtyp);
5814 break;
5817 if (chargeit) {
5818 /* FIXME: recharge only handles items in hero's inventory */
5819 if (u_carry)
5820 recharge(obj, 0);
5821 } else if (!skip) {
5822 char osym = obj->oclass; /* for checking glob of slime after it's
5823 destroyed */
5824 if (obj->in_use)
5825 --quan; /* one will be used up elsewhere */
5826 for (i = cnt = 0L; i < quan; i++)
5827 if (!rn2(3))
5828 cnt++;
5830 if (!cnt)
5831 return 0;
5833 if (u_carry || vis) {
5834 mult = (cnt == 1L) ? ((quan == 1L) ? "" /* 1 of 1 */
5835 : "One of ") /* 1 of N */
5836 : ((cnt < quan) ? "Some of " /* n of N */
5837 : (quan == 2L) ? "Both of " /* 2 of 2 */
5838 : "All of "); /* N of N */
5839 pline("%s%s %s!", mult,
5840 (cnt == 1L && quan == 1L) ? Yname2(obj) : yname(obj),
5841 destroy_strings[dindx][(cnt > 1L)]);
5843 if (u_carry) { /* effects that happen only to the player */
5844 if (osym == POTION_CLASS && dmgtyp != AD_COLD
5845 && (!breathless(gy.youmonst.data)
5846 || haseyes(gy.youmonst.data))) {
5847 potionbreathe(obj);
5849 if (obj->owornmask) { /* m_useup handles these for monster */
5850 if (obj->owornmask & W_RING) /* ring being worn */
5851 Ring_gone(obj);
5852 else
5853 setnotworn(obj);
5855 if (obj == gc.current_wand) {
5856 gc.current_wand = 0; /* destroyed */
5859 for (i = 0; i < cnt; i++) {
5860 if (u_carry)
5861 useup(obj);
5862 else
5863 m_useup(carrier, obj);
5865 if (dmg) {
5866 if (!u_carry) {
5867 return xresist ? 0 : dmg;
5869 if (xresist) {
5870 You("aren't hurt!");
5871 } else {
5872 const char *how = destroy_strings[dindx][2];
5873 boolean one = (cnt == 1L);
5875 if (dmgtyp == AD_FIRE && osym == FOOD_CLASS)
5876 how = "exploding glob of slime";
5877 losehp(dmg, one ? how : (const char *) makeplural(how),
5878 one ? KILLED_BY_AN : KILLED_BY);
5879 exercise(A_STR, FALSE);
5883 return dmg;
5886 /* scaling factor; dmg/5 stacks will be subjected to destroy_items() */
5887 #define DMG_DESTROY_SCALE 5
5888 /* largest amount of stacks that will be destroyed in a single call */
5889 #define MAX_ITEMS_DESTROYED 20
5891 /* target items of specified class in mon's inventory for possible destruction
5892 return total amount of damage inflicted, though this is unused if mon is
5893 the player */
5895 destroy_items(
5896 struct monst *mon, /* monster whose invent is being subjected to
5897 * destruction */
5898 int dmgtyp, /* AD_**** - currently only cold, fire, elec */
5899 int dmg_in) /* the amount of HP damage the attack dealt */
5901 struct obj *obj;
5902 int i, defer;
5903 int limit; /* max amount of item stacks destroyed, based on damage */
5904 struct {
5905 unsigned oid;
5906 struct obj *otmp;
5907 boolean deferred;
5908 } items_to_destroy[MAX_ITEMS_DESTROYED];
5909 int elig_stacks = 0; /* number of destroyable objects found so far */
5910 boolean u_carry = (mon == &gy.youmonst);
5911 /* this is a struct obj** because we might destroy the first item in it */
5912 struct obj **objchn = u_carry ? &gi.invent : &mon->minvent;
5913 int dmg_out = 0; /* damage caused by items getting destroyed */
5914 xint8 where = NOBJ_STATES;
5916 /* initialize items_to_destroy */
5917 for (i = 0; i < MAX_ITEMS_DESTROYED; ++i) {
5918 /* 0 should not be a valid o_id for anything */
5919 items_to_destroy[i].oid = 0;
5920 items_to_destroy[i].otmp = (struct obj *) 0;
5921 items_to_destroy[i].deferred = FALSE;
5924 /* don't straight up destroy all items with an equal chance; limit it
5925 based on the amount of damage being dealt by the source of the item
5926 destruction */
5927 limit = dmg_in / DMG_DESTROY_SCALE;
5928 if (dmg_in % DMG_DESTROY_SCALE > rn2(DMG_DESTROY_SCALE)) {
5929 limit++; /* dmg = 9: 20% chance of limit=1, 80% of limit=2, etc */
5931 if (limit > MAX_ITEMS_DESTROYED) {
5932 /* in case of incredibly high damage, prevent from overflowing
5933 * items_to_destroy */
5934 limit = MAX_ITEMS_DESTROYED;
5936 if (limit < 1) {
5937 return 0; /* nothing destroyed */
5940 /* Sometimes destroying an item can change inventory aside from
5941 * the item itself (cited case was a potion of unholy water; when
5942 * boiled, potionbreathe() caused hero to transform into were-beast
5943 * form and that resulted in dropping or destroying some worn armor).
5945 * Unlike other uses of the object bypass mechanism, destroy_items()
5946 * can be called multiple times for the same event. So we have to
5947 * explicitly clear it before each use and hope no other section of
5948 * code expects it to retain previous value.
5950 * Destruction of a ring of levitation or form change which pushes
5951 * off levitation boots could drop hero onto a fire trap that
5952 * could destroy other items and we'll get called recursively. Or
5953 * onto a trap which transports hero elsewhere, which won't disrupt
5954 * traversal but could yield message sequencing issues. So we
5955 * defer handling such things until after rest of inventory has
5956 * been processed. If some other combination of items and events
5957 * triggers a recursive call, rest of inventory after the triggering
5958 * item will be skipped by the outer call since the inner one will
5959 * have set the bypass bits of the whole list.
5961 * [Unfortunately, death while poly'd into flyer and subsequent
5962 * rehumanization could also drop hero onto a trap, and there's no
5963 * straightforward way to defer that. Things could be improved by
5964 * redoing this to use two passes, first to collect a list or array
5965 * of o_id and quantity of what is targeted for destruction,
5966 * second pass to handle the destruction.]
5968 bypass_objlist(*objchn, FALSE); /* clear bypass bit for invent */
5970 while ((obj = nxt_unbypassed_obj(*objchn)) != 0) {
5971 if (!destroyable(obj, dmgtyp))
5972 continue; /* this dmg type can't destroy this obj */
5974 /* obj is eligible; maybe add it to items_to_destroy */
5975 i = (elig_stacks < limit) ? elig_stacks : rn2(elig_stacks);
5976 /* do this afterwards to avoid not filling items_to_destroy[0] */
5977 elig_stacks++;
5978 if (i < 0 || i >= limit) {
5979 /* random index was too high; mollify analyzer by including < 0 */
5980 continue;
5982 items_to_destroy[i].oid = obj->o_id;
5983 items_to_destroy[i].otmp = obj;
5984 if (where == NOBJ_STATES)
5985 where = obj->where;
5986 else if (where != obj->where)
5987 impossible("destroy_item: items in multiple chains");
5989 /* if loss of this item might dump us onto a trap, hold off
5990 until later because potential recursive destroy_items() will
5991 result in setting bypass bits on whole chain--we would skip
5992 the rest as already processed once control returns here */
5993 if (u_carry
5994 && ((obj->owornmask != 0L
5995 && (objects[obj->otyp].oc_oprop == LEVITATION
5996 || objects[obj->otyp].oc_oprop == FLYING))
5997 /* destroyed wands and potions of polymorph don't trigger
5998 polymorph so don't need to be deferred */
5999 || (obj->otyp == POT_WATER && ismnum(u.ulycn)
6000 && (Upolyd ? obj->blessed : obj->cursed)))) {
6001 items_to_destroy[i].deferred = TRUE;
6002 } else {
6003 items_to_destroy[i].deferred = FALSE;
6006 if (elig_stacks > limit) {
6007 elig_stacks = limit; /* so we can loop up to elig_stacks */
6009 for (defer = 0; defer <= 1; ++defer) {
6010 /* if we saved some items for later (most likely just a worn ring
6011 of levitation) and they're still in inventory, handle them on the
6012 second iteration of the loop */
6013 for (i = 0; i < elig_stacks; ++i) {
6014 obj = items_to_destroy[i].otmp;
6015 if (obj && obj->o_id == items_to_destroy[i].oid
6016 && obj->where == where
6017 && (items_to_destroy[i].deferred == (defer == 1))) {
6018 dmg_out += maybe_destroy_item(mon, obj, dmgtyp);
6019 items_to_destroy[i].otmp = (struct obj *) 0;
6023 /* almost certainly not everything was destroyed; clear bypass bit after
6024 it was set earlier */
6025 bypass_objlist(*objchn, FALSE);
6026 return dmg_out;
6030 resist(struct monst *mtmp, char oclass, int damage, int tell)
6032 int resisted;
6033 int alev, dlev;
6035 /* fake players always pass resistance test against Conflict
6036 (this doesn't guarantee that they're never affected by it) */
6037 if (oclass == RING_CLASS && !damage && !tell && is_mplayer(mtmp->data))
6038 return 1;
6040 /* attack level */
6041 switch (oclass) {
6042 case WAND_CLASS:
6043 alev = 12;
6044 break;
6045 case TOOL_CLASS:
6046 alev = 10;
6047 break; /* instrument */
6048 case WEAPON_CLASS:
6049 alev = 10;
6050 break; /* artifact */
6051 case SCROLL_CLASS:
6052 alev = 9;
6053 break;
6054 case POTION_CLASS:
6055 alev = 6;
6056 break;
6057 case RING_CLASS:
6058 alev = 5;
6059 break;
6060 default:
6061 alev = u.ulevel;
6062 break; /* spell */
6064 /* defense level */
6065 dlev = (int) mtmp->m_lev;
6066 if (dlev > 50)
6067 dlev = 50;
6068 else if (dlev < 1)
6069 dlev = is_mplayer(mtmp->data) ? u.ulevel : 1;
6071 resisted = rn2(100 + alev - dlev) < mtmp->data->mr;
6072 if (resisted) {
6073 if (tell)
6074 shieldeff_mon(mtmp);
6075 damage = (damage + 1) / 2;
6078 if (damage) {
6079 mtmp->mhp -= damage;
6080 if (DEADMONSTER(mtmp)) {
6081 if (gm.m_using)
6082 monkilled(mtmp, "", AD_RBRE);
6083 else
6084 killed(mtmp);
6087 return resisted;
6090 #define MAXWISHTRY 5
6092 DISABLE_WARNING_FORMAT_NONLITERAL
6094 staticfn void
6095 wishcmdassist(int triesleft)
6097 static NEARDATA const char *
6098 wishinfo[] = {
6099 "Wish details:",
6101 "Enter the name of an object, such as \"potion of monster detection\",",
6102 "\"scroll labeled README\", \"elven mithril-coat\", or \"Grimtooth\"",
6103 "(without the quotes).",
6105 "For object types which come in stacks, you may specify a plural name",
6106 "such as \"potions of healing\", or specify a count, such as \"1000 gold",
6107 "pieces\", although that aspect of your wish might not be granted.",
6109 "You may also specify various prefix values which might be used to",
6110 "modify the item, such as \"uncursed\" or \"rustproof\" or \"+1\".",
6111 "Most modifiers shown when viewing your inventory can be specified.",
6113 "You may specify 'nothing' to explicitly decline this wish.",
6116 preserve_wishless[] = "Doing so will preserve 'wishless' conduct.",
6117 retry_info[] =
6118 "If you specify an unrecognized object name %s%s time%s,",
6119 retry_too[] = "a randomly chosen item will be granted.",
6120 suppress_cmdassist[] =
6121 "(Suppress this assistance with !cmdassist in your config file.)",
6122 *cardinals[] = { "zero", "one", "two", "three", "four", "five" },
6123 too_many[] = "too many";
6124 int i;
6125 winid win;
6126 char buf[BUFSZ];
6128 win = create_nhwindow(NHW_TEXT);
6129 if (!win)
6130 return;
6131 for (i = 0; i < SIZE(wishinfo) - 1; ++i)
6132 putstr(win, 0, wishinfo[i]);
6133 if (!u.uconduct.wishes)
6134 putstr(win, 0, preserve_wishless);
6135 putstr(win, 0, "");
6136 Sprintf(buf, retry_info,
6137 (triesleft >= 0 && triesleft < SIZE(cardinals))
6138 ? cardinals[triesleft]
6139 : too_many,
6140 (triesleft < MAXWISHTRY) ? " more" : "",
6141 plur(triesleft));
6142 putstr(win, 0, buf);
6143 putstr(win, 0, retry_too);
6144 putstr(win, 0, "");
6145 if (iflags.cmdassist)
6146 putstr(win, 0, suppress_cmdassist);
6147 display_nhwindow(win, TRUE);
6148 destroy_nhwindow(win);
6151 RESTORE_WARNING_FORMAT_NONLITERAL
6153 void
6154 makewish(void)
6156 char buf[BUFSZ] = DUMMY;
6157 char bufcpy[BUFSZ], wish[BUFSZ], promptbuf[QBUFSZ];
6158 struct obj *otmp, nothing;
6159 long maybe_LL_arti;
6160 int tries = 0;
6161 long oldwisharti = u.uconduct.wisharti;
6163 promptbuf[0] = '\0';
6164 nothing = cg.zeroobj; /* lint suppression; only its address matters */
6165 if (flags.verbose)
6166 You("may wish for an object.");
6167 retry:
6168 Strcpy(promptbuf, "For what do you wish");
6169 if (iflags.cmdassist && tries > 0)
6170 Strcat(promptbuf, " (enter 'help' for assistance)");
6171 Strcat(promptbuf, "?");
6172 getlin(promptbuf, buf);
6173 (void) mungspaces(buf);
6174 if (buf[0] == '\033') {
6175 buf[0] = '\0';
6176 } else if (!strcmpi(buf, "help")) {
6177 wishcmdassist(MAXWISHTRY - tries);
6178 buf[0] = '\0'; /* for EDIT_GETLIN */
6179 goto retry;
6182 * Note: if they wished for and got a non-object successfully,
6183 * otmp == &hands_obj. That includes an artifact which has been
6184 * denied. Wishing for "nothing" requires a separate value to remain
6185 * distinct.
6187 strcpy(bufcpy, buf);
6188 otmp = readobjnam(buf, &nothing);
6189 if (!otmp) {
6190 pline("Nothing fitting that description exists in the game.");
6191 if (++tries < MAXWISHTRY)
6192 goto retry;
6193 pline1(thats_enough_tries);
6194 otmp = readobjnam((char *) 0, (struct obj *) 0);
6195 if (!otmp)
6196 return; /* for safety; should never happen */
6197 } else if (otmp == &nothing) {
6198 /* explicitly wished for "nothing", presumably attempting
6199 to retain wishless conduct */
6200 livelog_printf(LL_WISH, "declined to make a wish");
6201 return;
6202 } else if (otmp == &hands_obj) {
6203 /* wizard mode terrain wish: skip livelogging, etc */
6204 return;
6207 if (otmp->oartifact) {
6208 /* update artifact bookkeeping; doesn't produce a livelog event */
6209 artifact_origin(otmp, ONAME_WISH | ONAME_KNOW_ARTI);
6212 /* wisharti conduct handled in readobjnam() */
6213 maybe_LL_arti = ((oldwisharti < u.uconduct.wisharti) ? LL_ARTIFACT : 0L);
6214 Snprintf(wish, sizeof wish, "\"%s\", got \"%s\"", bufcpy, doname(otmp));
6215 /* KMH, conduct */
6216 if (!u.uconduct.wishes++)
6217 livelog_printf((LL_CONDUCT | LL_WISH | maybe_LL_arti),
6218 "made %s first wish - %s", uhis(), wish);
6219 else if (!oldwisharti && u.uconduct.wisharti)
6220 livelog_printf((LL_CONDUCT | LL_WISH | LL_ARTIFACT),
6221 "made %s first artifact wish - %s", uhis(), wish);
6222 else
6223 livelog_printf((LL_WISH | maybe_LL_arti), "wished for %s", wish);
6224 /* TODO? maybe generate a second event describing what was received since
6225 these just echo player's request rather than show actual result */
6227 if (otmp->otyp == CORPSE && !u_safe_from_fatal_corpse(otmp, st_all))
6228 otmp->wishedfor = 1;
6230 const char *verb = ((Is_airlevel(&u.uz) || u.uinwater)
6231 ? "slip"
6232 : (otmp->otyp == CORPSE && otmp->wishedfor)
6233 ? "materialize" : "drop"),
6234 *oops_msg = (u.uswallow
6235 ? "Oops! %s out of your reach!"
6236 : (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
6237 || levl[u.ux][u.uy].typ < IRONBARS
6238 || levl[u.ux][u.uy].typ >= ICE)
6239 ? "Oops! %s away from you!"
6240 : !(otmp->otyp == CORPSE && otmp->wishedfor)
6241 ? "Oops! %s to the floor!"
6242 : "Careful! %s on the floor!");
6244 /* The(aobjnam()) is safe since otmp is unidentified -dlc */
6245 (void) hold_another_object(otmp, oops_msg, The(aobjnam(otmp, verb)),
6246 (const char *) 0);
6247 u.ublesscnt += rn1(100, 50); /* the gods take notice */
6250 /* Fills buf with the appropriate string for this ray.
6251 * In the hallucination case, insert "blast of <silly thing>".
6252 * Assumes that the caller will specify typ in the appropriate range for
6253 * wand/spell/breath weapon. */
6254 const char*
6255 flash_str(
6256 int typ,
6257 boolean nohallu) /* suppress hallucination (for death reasons) */
6259 static char fltxt[BUFSZ];
6261 typ = zaptype(typ);
6262 if (Hallucination && !nohallu) {
6263 /* always return "blast of foo" for simplicity;
6264 this could be extended with hallucinatory rays, but probably
6265 not worth it at this time */
6266 Sprintf(fltxt, "blast of %s", rnd_hallublast());
6267 } else {
6268 Strcpy(fltxt, flash_types[typ]);
6270 return fltxt;
6273 /*zap.c*/