1 /* NetHack 3.6 zap.c $NHDT-Date: 1470819844 2016/08/10 09:04:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.263 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
7 /* Disintegration rays have special treatment; corpses are never left.
8 * But the routine which calculates the damage is separate from the routine
9 * which kills the monster. The damage routine returns this cookie to
10 * indicate that the monster should be disintegrated.
12 #define MAGIC_COOKIE 1000
14 static NEARDATA boolean obj_zapped
;
15 static NEARDATA
int poly_zapped
;
17 extern boolean notonhead
; /* for long worms */
19 /* kludge to use mondied instead of killed */
20 extern boolean m_using
;
22 STATIC_DCL
void FDECL(polyuse
, (struct obj
*, int, int));
23 STATIC_DCL
void FDECL(create_polymon
, (struct obj
*, int));
24 STATIC_DCL
int FDECL(stone_to_flesh_obj
, (struct obj
*));
25 STATIC_DCL boolean
FDECL(zap_updown
, (struct obj
*));
26 STATIC_DCL
void FDECL(zhitu
, (int, int, const char *, XCHAR_P
, XCHAR_P
));
27 STATIC_DCL
void FDECL(revive_egg
, (struct obj
*));
28 STATIC_DCL boolean
FDECL(zap_steed
, (struct obj
*));
29 STATIC_DCL
void FDECL(skiprange
, (int, int *, int *));
31 STATIC_DCL
int FDECL(zap_hit
, (int, int));
32 STATIC_OVL
void FDECL(disintegrate_mon
, (struct monst
*, int, const char *));
33 STATIC_DCL
void FDECL(backfire
, (struct obj
*));
34 STATIC_DCL
int FDECL(spell_hit_bonus
, (int));
36 #define ZT_MAGIC_MISSILE (AD_MAGM - 1)
37 #define ZT_FIRE (AD_FIRE - 1)
38 #define ZT_COLD (AD_COLD - 1)
39 #define ZT_SLEEP (AD_SLEE - 1)
40 #define ZT_DEATH (AD_DISN - 1) /* or disintegration */
41 #define ZT_LIGHTNING (AD_ELEC - 1)
42 #define ZT_POISON_GAS (AD_DRST - 1)
43 #define ZT_ACID (AD_ACID - 1)
44 /* 8 and 9 are currently unassigned */
46 #define ZT_WAND(x) (x)
47 #define ZT_SPELL(x) (10 + (x))
48 #define ZT_BREATH(x) (20 + (x))
50 #define is_hero_spell(type) ((type) >= 10 && (type) < 20)
52 #define M_IN_WATER(ptr) \
53 ((ptr)->mlet == S_EEL || amphibious(ptr) || is_swimmer(ptr))
55 STATIC_VAR
const char are_blinded_by_the_flash
[] =
56 "are blinded by the flash!";
58 const char *const flash_types
[] = /* also used in buzzmu(mcastu.c) */
60 "magic missile", /* Wands must be 0-9 */
61 "bolt of fire", "bolt of cold", "sleep ray", "death ray",
62 "bolt of lightning", "", "", "", "",
64 "magic missile", /* Spell equivalents must be 10-19 */
65 "fireball", "cone of cold", "sleep ray", "finger of death",
66 "bolt of lightning", /* there is no spell, used for retribution */
69 "blast of missiles", /* Dragon breath equivalents 20-29*/
70 "blast of fire", "blast of frost", "blast of sleep gas",
71 "blast of disintegration", "blast of lightning",
72 "blast of poison gas", "blast of acid", "", ""
76 * Recognizing unseen wands by zapping: in 3.4.3 and earlier, zapping
77 * most wand types while blind would add that type to the discoveries
78 * list even if it had never been seen (ie, picked up while blinded
79 * and shown in inventory as simply "a wand"). This behavior has been
80 * changed; now such wands won't be discovered. But if the type is
81 * already discovered, then the individual wand whose effect was just
82 * observed will be flagged as if seen. [You already know wands of
83 * striking; you zap "a wand" and observe striking effect (presumably
84 * by sound or touch); it'll become shown in inventory as "a wand of
87 * Unfortunately, the new behavior isn't really correct either. There
88 * should be an `eknown' bit for "effect known" added for wands (and
89 * for potions since quaffing one of a stack is similar) so that the
90 * particular wand which has been zapped would have its type become
91 * known (it would change from "a wand" to "a wand of striking", for
92 * example) without the type becoming discovered or other unknown wands
93 * of that type showing additional information. When blindness ends,
94 * all objects in inventory with the eknown bit set would be discovered
95 * and other items of the same type would become known as such.
98 /* wand discovery gets special handling when hero is blinded */
103 /* For a wand (or wand-like tool) zapped by the player, if the
104 effect was observable (determined by caller; usually seen, but
105 possibly heard or felt if the hero is blinded) then discover the
106 object type provided that the object itself is known (as more
107 than just "a wand"). If object type is already discovered and
108 we observed the effect, mark the individual wand as having been
109 seen. Suppress spells (which use fake spellbook object for `obj')
110 so that casting a spell won't re-discover its forgotten book. */
111 if (obj
->oclass
!= SPBOOK_CLASS
) {
112 /* if type already discovered, treat this item has having been seen
113 even if hero is currently blinded (skips redundant makeknown) */
114 if (objects
[obj
->otyp
].oc_name_known
) {
115 obj
->dknown
= 1; /* will usually be set already */
117 /* otherwise discover it if item itself has been or can be seen */
119 /* in case it was picked up while blind and then zapped without
120 examining inventory after regaining sight (bypassing xname) */
123 /* make the discovery iff we know what we're manipulating */
125 makeknown(obj
->otyp
);
130 /* Routines for IMMEDIATE wands and spells. */
131 /* bhitm: monster mtmp was hit by the effect of wand or spell otmp */
137 boolean wake
= TRUE
; /* Most 'zaps' should wake monster */
138 boolean reveal_invis
= FALSE
, learn_it
= FALSE
;
139 boolean dbldam
= Role_if(PM_KNIGHT
) && u
.uhave
.questart
;
140 int dmg
, otyp
= otmp
->otyp
;
141 const char *zap_type_text
= "spell";
143 boolean disguised_mimic
= (mtmp
->data
->mlet
== S_MIMIC
144 && mtmp
->m_ap_type
!= M_AP_NOTHING
);
146 if (u
.uswallow
&& mtmp
== u
.ustuck
)
147 reveal_invis
= FALSE
;
149 notonhead
= (mtmp
->mx
!= bhitpos
.x
|| mtmp
->my
!= bhitpos
.y
);
152 zap_type_text
= "wand";
158 if (resists_magm(mtmp
)) { /* match effect on player */
159 shieldeff(mtmp
->mx
, mtmp
->my
);
161 break; /* skip makeknown */
162 } else if (u
.uswallow
|| rnd(20) < 10 + find_mac(mtmp
)) {
166 if (otyp
== SPE_FORCE_BOLT
)
167 dmg
= spell_damage_bonus(dmg
);
168 hit(zap_type_text
, mtmp
, exclam(dmg
));
169 (void) resist(mtmp
, otmp
->oclass
, dmg
, TELL
);
171 miss(zap_type_text
, mtmp
);
174 case WAN_SLOW_MONSTER
:
175 case SPE_SLOW_MONSTER
:
176 if (!resist(mtmp
, otmp
->oclass
, 0, NOTELL
)) {
179 mon_adjust_speed(mtmp
, -1, otmp
);
180 m_dowear(mtmp
, FALSE
); /* might want speed boots */
181 if (u
.uswallow
&& (mtmp
== u
.ustuck
) && is_whirly(mtmp
->data
)) {
182 You("disrupt %s!", mon_nam(mtmp
));
183 pline("A huge hole opens up...");
184 expels(mtmp
, mtmp
->data
, TRUE
);
188 case WAN_SPEED_MONSTER
:
189 if (!resist(mtmp
, otmp
->oclass
, 0, NOTELL
)) {
192 mon_adjust_speed(mtmp
, 1, otmp
);
193 m_dowear(mtmp
, FALSE
); /* might want speed boots */
196 case WAN_UNDEAD_TURNING
:
197 case SPE_TURN_UNDEAD
:
199 if (unturn_dead(mtmp
))
201 if (is_undead(mtmp
->data
) || is_vampshifter(mtmp
)) {
207 if (otyp
== SPE_TURN_UNDEAD
)
208 dmg
= spell_damage_bonus(dmg
);
209 context
.bypasses
= TRUE
; /* for make_corpse() */
210 if (!resist(mtmp
, otmp
->oclass
, dmg
, NOTELL
)) {
212 monflee(mtmp
, 0, FALSE
, TRUE
);
219 if (resists_magm(mtmp
)) {
220 /* magic resistance protects from polymorph traps, so make
221 it guard against involuntary polymorph attacks too... */
222 shieldeff(mtmp
->mx
, mtmp
->my
);
223 } else if (!resist(mtmp
, otmp
->oclass
, 0, NOTELL
)) {
224 boolean polyspot
= (otyp
!= POT_POLYMORPH
),
225 give_msg
= (!Hallucination
227 || (u
.uswallow
&& mtmp
== u
.ustuck
)));
229 /* dropped inventory (due to death by system shock,
230 or loss of wielded weapon and/or worn armor due to
231 limitations of new shape) won't be hit by this zap */
233 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
235 /* natural shapechangers aren't affected by system shock
236 (unless protection from shapechangers is interfering
237 with their metabolism...) */
238 if (mtmp
->cham
== NON_PM
&& !rn2(25)) {
239 if (canseemon(mtmp
)) {
240 pline("%s shudders!", Monnam(mtmp
));
243 /* context.bypasses = TRUE; ## for make_corpse() */
244 /* no corpse after system shock */
245 xkilled(mtmp
, XKILL_GIVEMSG
| XKILL_NOCORPSE
);
246 } else if (newcham(mtmp
, (struct permonst
*) 0,
247 polyspot
, give_msg
) != 0
248 /* if shapechange failed because there aren't
249 enough eligible candidates (most likely for
250 vampshifter), try reverting to original form */
251 || (mtmp
->cham
>= LOW_PM
252 && newcham(mtmp
, &mons
[mtmp
->cham
],
253 polyspot
, give_msg
) != 0)) {
254 if (give_msg
&& (canspotmon(mtmp
)
255 || (u
.uswallow
&& mtmp
== u
.ustuck
)))
260 case WAN_CANCELLATION
:
261 case SPE_CANCELLATION
:
264 (void) cancel_monst(mtmp
, otmp
, TRUE
, TRUE
, FALSE
);
266 case WAN_TELEPORTATION
:
267 case SPE_TELEPORT_AWAY
:
270 reveal_invis
= !u_teleport_mon(mtmp
, TRUE
);
272 case WAN_MAKE_INVISIBLE
: {
273 int oldinvis
= mtmp
->minvis
;
278 /* format monster's name before altering its visibility */
279 Strcpy(nambuf
, Monnam(mtmp
));
280 mon_set_minvis(mtmp
);
281 if (!oldinvis
&& knowninvisible(mtmp
)) {
282 pline("%s turns transparent!", nambuf
);
289 case SPE_WIZARD_LOCK
:
290 wake
= closeholdingtrap(mtmp
, &learn_it
);
300 wake
= FALSE
; /* don't want immediate counterattack */
301 if (u
.uswallow
&& mtmp
== u
.ustuck
) {
302 if (is_animal(mtmp
->data
)) {
304 You_feel("a sudden rush of air!");
306 pline("%s opens its mouth!", Monnam(mtmp
));
308 expels(mtmp
, mtmp
->data
, TRUE
);
309 /* zap which hits steed will only release saddle if it
310 doesn't hit a holding or falling trap; playability
311 here overrides the more logical target ordering */
312 } else if (openholdingtrap(mtmp
, &learn_it
)) {
314 } else if (openfallingtrap(mtmp
, TRUE
, &learn_it
)) {
315 /* mtmp might now be on the migrating monsters list */
317 } else if ((obj
= which_armor(mtmp
, W_SADDLE
)) != 0) {
320 Sprintf(buf
, "%s %s", s_suffix(Monnam(mtmp
)),
321 distant_name(obj
, xname
));
322 if (cansee(mtmp
->mx
, mtmp
->my
)) {
323 if (!canspotmon(mtmp
))
324 Strcpy(buf
, An(distant_name(obj
, xname
)));
325 pline("%s falls to the %s.", buf
,
326 surface(mtmp
->mx
, mtmp
->my
));
327 } else if (canspotmon(mtmp
)) {
328 pline("%s falls off.", buf
);
330 obj_extract_self(obj
);
331 mdrop_obj(mtmp
, obj
, FALSE
);
335 case SPE_EXTRA_HEALING
:
337 if (mtmp
->data
!= &mons
[PM_PESTILENCE
]) {
338 wake
= FALSE
; /* wakeup() makes the target angry */
339 mtmp
->mhp
+= d(6, otyp
== SPE_EXTRA_HEALING
? 8 : 4);
340 if (mtmp
->mhp
> mtmp
->mhpmax
)
341 mtmp
->mhp
= mtmp
->mhpmax
;
342 if (mtmp
->mblinded
) {
346 if (canseemon(mtmp
)) {
347 if (disguised_mimic
) {
348 if (is_obj_mappear(mtmp
,STRANGE_OBJECT
)) {
349 /* it can do better now */
351 newsym(mtmp
->mx
, mtmp
->my
);
353 mimic_hit_msg(mtmp
, otyp
);
355 pline("%s looks%s better.", Monnam(mtmp
),
356 otyp
== SPE_EXTRA_HEALING
? " much" : "");
358 if (mtmp
->mtame
|| mtmp
->mpeaceful
) {
359 adjalign(Role_if(PM_HEALER
) ? 1 : sgn(u
.ualign
.type
));
361 } else { /* Pestilence */
362 /* Pestilence will always resist; damage is half of 3d{4,8} */
363 (void) resist(mtmp
, otmp
->oclass
,
364 d(3, otyp
== SPE_EXTRA_HEALING
? 8 : 4), TELL
);
367 case WAN_LIGHT
: /* (broken wand) */
368 if (flash_hits_mon(mtmp
, otmp
)) {
373 case WAN_SLEEP
: /* (broken wand) */
374 /* [wakeup() doesn't rouse victims of temporary sleep,
375 so it's okay to leave `wake' set to TRUE here] */
377 if (sleep_monst(mtmp
, d(1 + otmp
->spe
, 12), WAND_CLASS
))
382 case SPE_STONE_TO_FLESH
:
383 if (monsndx(mtmp
->data
) == PM_STONE_GOLEM
) {
384 char *name
= Monnam(mtmp
);
386 /* turn into flesh golem */
387 if (newcham(mtmp
, &mons
[PM_FLESH_GOLEM
], FALSE
, FALSE
)) {
389 pline("%s turns to flesh!", name
);
392 pline("%s looks rather fleshy for a moment.", name
);
400 dmg
= monhp_per_lvl(mtmp
);
403 if (otyp
== SPE_DRAIN_LIFE
)
404 dmg
= spell_damage_bonus(dmg
);
405 if (resists_drli(mtmp
)) {
406 shieldeff(mtmp
->mx
, mtmp
->my
);
407 } else if (!resist(mtmp
, otmp
->oclass
, dmg
, NOTELL
) && mtmp
->mhp
> 0) {
410 /* die if already level 0, regardless of hit points */
411 if (mtmp
->mhp
<= 0 || mtmp
->mhpmax
<= 0 || mtmp
->m_lev
< 1) {
416 pline("%s suddenly seems weaker!", Monnam(mtmp
));
424 impossible("What an interesting effect (%d)", otyp
);
431 if (mtmp
->isshk
&& !*u
.ushops
)
433 } else if (mtmp
->m_ap_type
)
434 seemimic(mtmp
); /* might unblock if mimicing a boulder/door */
436 /* note: bhitpos won't be set if swallowed, but that's okay since
437 * reveal_invis will be false. We can't use mtmp->mx, my since it
438 * might be an invisible worm hit on the tail.
441 if (mtmp
->mhp
> 0 && cansee(bhitpos
.x
, bhitpos
.y
)
442 && !canspotmon(mtmp
))
443 map_invisible(bhitpos
.x
, bhitpos
.y
);
445 /* if effect was observable then discover the wand type provided
446 that the wand itself has been seen */
460 return; /* don't show minvent for long worm tail */
463 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
) {
464 otmp
->dknown
= 1; /* treat as "seen" */
465 if (Is_container(otmp
) || otmp
->otyp
== STATUE
) {
467 if (!SchroedingersBox(otmp
))
471 (void) display_minventory(mtmp
, MINV_ALL
| MINV_NOLET
| PICK_NONE
,
474 pline("%s is not carrying anything%s.", noit_Monnam(mtmp
),
475 (u
.uswallow
&& mtmp
== u
.ustuck
) ? " besides you" : "");
480 * Return the object's physical location. This only makes sense for
481 * objects that are currently on the level (i.e. migrating objects
482 * are nowhere). By default, only things that can be seen (in hero's
483 * inventory, monster's inventory, or on the ground) are reported.
484 * By adding BURIED_TOO and/or CONTAINED_TOO flags, you can also get
485 * the location of buried and contained objects. Note that if an
486 * object is carried by a monster, its reported position may change
487 * from turn to turn. This function returns FALSE if the position
488 * is not available or subject to the constraints above.
491 get_obj_location(obj
, xp
, yp
, locflags
)
496 switch (obj
->where
) {
506 if (obj
->ocarry
->mx
) {
507 *xp
= obj
->ocarry
->mx
;
508 *yp
= obj
->ocarry
->my
;
511 break; /* !mx => migrating monster */
513 if (locflags
& BURIED_TOO
) {
520 if (locflags
& CONTAINED_TOO
)
521 return get_obj_location(obj
->ocontainer
, xp
, yp
, locflags
);
529 get_mon_location(mon
, xp
, yp
, locflags
)
532 int locflags
; /* non-zero means get location even if monster is buried */
534 if (mon
== &youmonst
) {
538 } else if (mon
->mx
> 0 && (!mon
->mburied
|| locflags
)) {
542 } else { /* migrating or buried */
548 /* used by revive() and animate_statue() */
554 struct monst
*mtmp
= (struct monst
*) 0;
555 struct monst
*mtmp2
= (struct monst
*) 0;
558 mtmp2
= get_mtraits(obj
, TRUE
);
560 /* save_mtraits() validated mtmp2->mnum */
561 mtmp2
->data
= &mons
[mtmp2
->mnum
];
562 if (mtmp2
->mhpmax
<= 0 && !is_rider(mtmp2
->data
))
563 return (struct monst
*) 0;
564 mtmp
= makemon(mtmp2
->data
, cc
->x
, cc
->y
,
565 NO_MINVENT
| MM_NOWAIT
| MM_NOCOUNTBIRTH
);
569 /* heal the monster */
570 if (mtmp
->mhpmax
> mtmp2
->mhpmax
&& is_rider(mtmp2
->data
))
571 mtmp2
->mhpmax
= mtmp
->mhpmax
;
572 mtmp2
->mhp
= mtmp2
->mhpmax
;
573 /* Get these ones from mtmp */
574 mtmp2
->minvent
= mtmp
->minvent
; /*redundant*/
575 /* monster ID is available if the monster died in the current
576 game, but will be zero if the corpse was in a bones level
577 (we cleared it when loading bones) */
579 mtmp2
->m_id
= mtmp
->m_id
;
580 /* might be bringing quest leader back to life */
581 if (quest_status
.leader_is_dead
582 /* leader_is_dead implies leader_m_id is valid */
583 && mtmp2
->m_id
== quest_status
.leader_m_id
)
584 quest_status
.leader_is_dead
= FALSE
;
586 mtmp2
->mx
= mtmp
->mx
;
587 mtmp2
->my
= mtmp
->my
;
588 mtmp2
->mux
= mtmp
->mux
;
589 mtmp2
->muy
= mtmp
->muy
;
590 mtmp2
->mw
= mtmp
->mw
;
591 mtmp2
->wormno
= mtmp
->wormno
;
592 mtmp2
->misc_worn_check
= mtmp
->misc_worn_check
;
593 mtmp2
->weapon_check
= mtmp
->weapon_check
;
594 mtmp2
->mtrapseen
= mtmp
->mtrapseen
;
595 mtmp2
->mflee
= mtmp
->mflee
;
596 mtmp2
->mburied
= mtmp
->mburied
;
597 mtmp2
->mundetected
= mtmp
->mundetected
;
598 mtmp2
->mfleetim
= mtmp
->mfleetim
;
599 mtmp2
->mlstmv
= mtmp
->mlstmv
;
600 mtmp2
->m_ap_type
= mtmp
->m_ap_type
;
601 /* set these ones explicitly */
607 mtmp2
->msleeping
= 0;
610 /* most cancelled monsters return to normal,
611 but some need to stay cancelled */
612 if (!dmgtype(mtmp2
->data
, AD_SEDU
)
613 && (!SYSOPT_SEDUCE
|| !dmgtype(mtmp2
->data
, AD_SSEX
)))
615 mtmp2
->mcansee
= 1; /* set like in makemon */
619 /* when traits are for a shopeekper, dummy monster 'mtmp' won't
620 have necessary eshk data for replmon() -> replshk() */
623 *ESHK(mtmp
) = *ESHK(mtmp2
);
624 if (ESHK(mtmp2
)->bill_p
!= 0
625 && ESHK(mtmp2
)->bill_p
!= (struct bill_x
*) -1000)
626 ESHK(mtmp
)->bill_p
= &(ESHK(mtmp
)->bill
[0]);
629 replmon(mtmp
, mtmp2
);
630 newsym(mtmp2
->mx
, mtmp2
->my
); /* Might now be invisible */
632 /* in case Protection_from_shape_changers is different
633 now than it was when the traits were stored */
640 * get_container_location() returns the following information
641 * about the outermost container:
642 * loc argument gets set to:
643 * OBJ_INVENT if in hero's inventory; return 0.
644 * OBJ_FLOOR if on the floor; return 0.
645 * OBJ_BURIED if buried; return 0.
646 * OBJ_MINVENT if in monster's inventory; return monster.
647 * container_nesting is updated with the nesting depth of the containers
651 get_container_location(obj
, loc
, container_nesting
)
654 int *container_nesting
;
659 if (container_nesting
)
660 *container_nesting
= 0;
661 while (obj
&& obj
->where
== OBJ_CONTAINED
) {
662 if (container_nesting
)
663 *container_nesting
+= 1;
664 obj
= obj
->ocontainer
;
667 *loc
= obj
->where
; /* outermost container's location */
668 if (obj
->where
== OBJ_MINVENT
)
671 return (struct monst
*) 0;
675 * Attempt to revive the given corpse, return the revived monster if
676 * successful. Note: this does NOT use up the corpse if it fails.
677 * If corpse->quan is more than 1, only one corpse will be affected
678 * and only one monster will be resurrected.
681 revive(corpse
, by_hero
)
685 struct monst
*mtmp
= 0;
686 struct permonst
*mptr
;
687 struct obj
*container
;
691 int montype
, container_nesting
= 0;
693 if (corpse
->otyp
!= CORPSE
) {
694 impossible("Attempting to revive %s?", xname(corpse
));
695 return (struct monst
*) 0;
699 if (corpse
->where
!= OBJ_CONTAINED
) {
700 /* only for invent, minvent, or floor */
702 (void) get_obj_location(corpse
, &x
, &y
, 0);
704 /* deal with corpses in [possibly nested] containers */
705 struct monst
*carrier
;
706 int holder
= OBJ_FREE
;
708 container
= corpse
->ocontainer
;
710 get_container_location(container
, &holder
, &container_nesting
);
713 x
= carrier
->mx
, y
= carrier
->my
;
719 (void) get_obj_location(corpse
, &x
, &y
, CONTAINED_TOO
);
722 break; /* x,y are 0 */
726 /* Rules for revival from containers:
727 * - the container cannot be locked
728 * - the container cannot be heavily nested (>2 is arbitrary)
729 * - the container cannot be a statue or bag of holding
730 * (except in very rare cases for the latter)
732 || (container
&& (container
->olocked
|| container_nesting
> 2
733 || container
->otyp
== STATUE
734 || (container
->otyp
== BAG_OF_HOLDING
&& rn2(40)))))
735 return (struct monst
*) 0;
737 /* record the object's location now that we're sure where it is */
738 corpse
->ox
= x
, corpse
->oy
= y
;
740 /* prepare for the monster */
741 montype
= corpse
->corpsenm
;
742 mptr
= &mons
[montype
];
743 /* [should probably handle recorporealization first; if corpse and
744 ghost are at same location, revived creature shouldn't be bumped
745 to an adjacent spot by ghost which joins with it] */
747 if (enexto(&xy
, x
, y
, mptr
))
751 if (mons
[montype
].mlet
== S_EEL
&& !IS_POOL(levl
[x
][y
].typ
)) {
752 if (by_hero
&& cansee(x
,y
))
753 pline("%s twitches feebly.",
754 upstart(corpse_xname(corpse
, (const char *) 0, CXN_PFX_THE
)));
755 return (struct monst
*) 0;
758 if (cant_revive(&montype
, TRUE
, corpse
)) {
759 /* make a zombie or doppelganger instead */
760 /* note: montype has changed; mptr keeps old value for newcham() */
761 mtmp
= makemon(&mons
[montype
], x
, y
, NO_MINVENT
| MM_NOWAIT
);
763 /* skip ghost handling */
764 if (has_omid(corpse
))
766 if (has_omonst(corpse
))
768 if (mtmp
->cham
== PM_DOPPELGANGER
) {
769 /* change shape to match the corpse */
770 (void) newcham(mtmp
, mptr
, FALSE
, FALSE
);
771 } else if (mtmp
->data
->mlet
== S_ZOMBIE
) {
772 mtmp
->mhp
= mtmp
->mhpmax
= 100;
773 mon_adjust_speed(mtmp
, 2, (struct obj
*) 0); /* MFAST */
776 } else if (has_omonst(corpse
)) {
777 /* use saved traits */
779 mtmp
= montraits(corpse
, &xy
);
780 if (mtmp
&& mtmp
->mtame
&& !mtmp
->isminion
)
781 wary_dog(mtmp
, TRUE
);
783 /* make a new monster */
784 mtmp
= makemon(mptr
, x
, y
, NO_MINVENT
| MM_NOWAIT
| MM_NOCOUNTBIRTH
);
787 return (struct monst
*) 0;
789 /* hiders shouldn't already be re-hidden when they revive */
790 if (mtmp
->mundetected
) {
791 mtmp
->mundetected
= 0;
792 newsym(mtmp
->mx
, mtmp
->my
);
797 one_of
= (corpse
->quan
> 1L);
799 corpse
= splitobj(corpse
, 1L);
801 /* if this is caused by the hero there might be a shop charge */
803 struct monst
*shkp
= 0;
805 x
= corpse
->ox
, y
= corpse
->oy
;
806 if (costly_spot(x
, y
)
807 && (carried(corpse
) ? corpse
->unpaid
: !corpse
->no_charge
))
808 shkp
= shop_keeper(*in_rooms(x
, y
, SHOPBASE
));
812 unsigned pfx
= CXN_PFX_THE
;
814 Strcpy(buf
, one_of
? "one of " : "");
815 if (carried(corpse
) && !corpse
->unpaid
) {
816 Strcat(buf
, "your ");
820 corpse
->quan
++; /* force plural */
821 Strcat(buf
, corpse_xname(corpse
, (const char *) 0, pfx
));
822 if (one_of
) /* could be simplified to ''corpse->quan = 1L;'' */
824 pline("%s glows iridescently.", upstart(buf
));
826 /* need some prior description of the corpse since
827 stolen_value() will refer to the object as "it" */
828 pline("A corpse is resuscitated.");
830 /* don't charge for shopkeeper's own corpse if we just revived him */
831 if (shkp
&& mtmp
!= shkp
)
832 (void) stolen_value(corpse
, x
, y
, (boolean
) shkp
->mpeaceful
,
835 /* [we don't give any comparable message about the corpse for
836 the !by_hero case because caller might have already done so] */
839 /* handle recorporealization of an active ghost */
840 if (has_omid(corpse
)) {
845 (void) memcpy((genericptr_t
) &m_id
, (genericptr_t
) OMID(corpse
),
847 ghost
= find_mid(m_id
, FM_FMON
);
848 if (ghost
&& ghost
->data
== &mons
[PM_GHOST
]) {
849 if (canseemon(ghost
))
850 pline("%s is suddenly drawn into its former body!",
852 /* transfer the ghost's inventory along with it */
853 while ((otmp
= ghost
->minvent
) != 0) {
854 obj_extract_self(otmp
);
855 add_to_minv(mtmp
, otmp
);
857 /* tame the revived monster if its ghost was tame */
858 if (ghost
->mtame
&& !mtmp
->mtame
) {
859 if (tamedog(mtmp
, (struct obj
*) 0)) {
860 /* ghost's edog data is ignored */
861 mtmp
->mtame
= ghost
->mtame
;
864 /* was ghost, now alive, it's all very confusing */
866 /* separate ghost monster no longer exists */
872 /* monster retains its name */
873 if (has_oname(corpse
) && !unique_corpstat(mtmp
->data
))
874 mtmp
= christen_monst(mtmp
, ONAME(corpse
));
875 /* partially eaten corpse yields wounded monster */
877 mtmp
->mhp
= eaten_stat(mtmp
->mhp
, corpse
);
878 /* track that this monster was revived at least once */
881 /* finally, get rid of the corpse--it's gone now */
882 switch (corpse
->where
) {
887 /* in case MON_AT+enexto for invisible mon */
888 x
= corpse
->ox
, y
= corpse
->oy
;
889 /* not useupf(), which charges */
890 if (corpse
->quan
> 1L)
891 corpse
= splitobj(corpse
, 1L);
896 m_useup(corpse
->ocarry
, corpse
);
899 obj_extract_self(corpse
);
900 obfree(corpse
, (struct obj
*) 0);
914 * Note: generic eggs with corpsenm set to NON_PM will never hatch.
916 if (obj
->otyp
!= EGG
)
918 if (obj
->corpsenm
!= NON_PM
&& !dead_species(obj
->corpsenm
, TRUE
))
919 attach_egg_hatch_timeout(obj
, 0L);
922 /* try to revive all corpses and eggs carried by `mon' */
927 struct obj
*otmp
, *otmp2
;
929 char owner
[BUFSZ
], corpse
[BUFSZ
];
933 youseeit
= (mon
== &youmonst
) ? TRUE
: canseemon(mon
);
934 otmp2
= (mon
== &youmonst
) ? invent
: mon
->minvent
;
935 owner
[0] = corpse
[0] = '\0'; /* lint suppression */
937 while ((otmp
= otmp2
) != 0) {
939 if (otmp
->otyp
== EGG
)
941 if (otmp
->otyp
!= CORPSE
)
943 /* save the name; the object is liable to go away */
946 corpse_xname(otmp
, (const char *) 0, CXN_SINGULAR
));
947 Shk_Your(owner
, otmp
); /* includes a trailing space */
950 /* for a stack, only one is revived */
951 if ((mtmp2
= revive(otmp
, !context
.mon_moving
)) != 0) {
954 pline("%s%s suddenly comes alive!", owner
, corpse
);
955 else if (canseemon(mtmp2
))
956 pline("%s suddenly appears!", Amonnam(mtmp2
));
962 /* cancel obj, possibly carried by you or a monster */
965 register struct obj
*obj
;
967 boolean u_ring
= (obj
== uleft
|| obj
== uright
);
968 int otyp
= obj
->otyp
;
971 case RIN_GAIN_STRENGTH
:
972 if ((obj
->owornmask
& W_RING
) && u_ring
) {
973 ABON(A_STR
) -= obj
->spe
;
977 case RIN_GAIN_CONSTITUTION
:
978 if ((obj
->owornmask
& W_RING
) && u_ring
) {
979 ABON(A_CON
) -= obj
->spe
;
984 if ((obj
->owornmask
& W_RING
) && u_ring
) {
985 ABON(A_CHA
) -= obj
->spe
;
989 case RIN_INCREASE_ACCURACY
:
990 if ((obj
->owornmask
& W_RING
) && u_ring
)
991 u
.uhitinc
-= obj
->spe
;
993 case RIN_INCREASE_DAMAGE
:
994 if ((obj
->owornmask
& W_RING
) && u_ring
)
995 u
.udaminc
-= obj
->spe
;
997 case GAUNTLETS_OF_DEXTERITY
:
998 if ((obj
->owornmask
& W_ARMG
) && (obj
== uarmg
)) {
999 ABON(A_DEX
) -= obj
->spe
;
1003 case HELM_OF_BRILLIANCE
:
1004 if ((obj
->owornmask
& W_ARMH
) && (obj
== uarmh
)) {
1005 ABON(A_INT
) -= obj
->spe
;
1006 ABON(A_WIS
) -= obj
->spe
;
1010 /* case RIN_PROTECTION: not needed */
1012 if (objects
[otyp
].oc_magic
1013 || (obj
->spe
&& (obj
->oclass
== ARMOR_CLASS
1014 || obj
->oclass
== WEAPON_CLASS
|| is_weptool(obj
)))
1016 || otyp
== POT_SICKNESS
1017 || (otyp
== POT_WATER
&& (obj
->blessed
|| obj
->cursed
))) {
1018 if (obj
->spe
!= ((obj
->oclass
== WAND_CLASS
) ? -1 : 0)
1019 && otyp
!= WAN_CANCELLATION
/* can't cancel cancellation */
1020 && otyp
!= MAGIC_LAMP
/* cancelling doesn't remove djinni */
1021 && otyp
!= CANDELABRUM_OF_INVOCATION
) {
1022 costly_alteration(obj
, COST_CANCEL
);
1023 obj
->spe
= (obj
->oclass
== WAND_CLASS
) ? -1 : 0;
1025 switch (obj
->oclass
) {
1027 costly_alteration(obj
, COST_CANCEL
);
1028 obj
->otyp
= SCR_BLANK_PAPER
;
1032 if (otyp
!= SPE_CANCELLATION
&& otyp
!= SPE_NOVEL
1033 && otyp
!= SPE_BOOK_OF_THE_DEAD
) {
1034 costly_alteration(obj
, COST_CANCEL
);
1035 obj
->otyp
= SPE_BLANK_PAPER
;
1039 costly_alteration(obj
,
1042 : obj
->cursed
? COST_UNCURS
: COST_UNBLSS
);
1043 if (otyp
== POT_SICKNESS
|| otyp
== POT_SEE_INVISIBLE
) {
1044 /* sickness is "biologically contaminated" fruit juice;
1045 cancel it and it just becomes fruit juice...
1046 whereas see invisible tastes like "enchanted" fruit
1047 juice, it similarly cancels */
1048 obj
->otyp
= POT_FRUIT_JUICE
;
1050 obj
->otyp
= POT_WATER
;
1051 obj
->odiluted
= 0; /* same as any other water */
1061 /* Remove a positive enchantment or charge from obj,
1062 * possibly carried by you or a monster
1065 drain_item(obj
, by_you
)
1071 /* Is this a charged/enchanted object? */
1073 || (!objects
[obj
->otyp
].oc_charged
&& obj
->oclass
!= WEAPON_CLASS
1074 && obj
->oclass
!= ARMOR_CLASS
&& !is_weptool(obj
))
1077 if (defends(AD_DRLI
, obj
) || defends_when_carried(AD_DRLI
, obj
)
1078 || obj_resists(obj
, 10, 90))
1081 /* Charge for the cost of the object */
1083 costly_alteration(obj
, COST_DRAIN
);
1085 /* Drain the object and any implied effects */
1087 u_ring
= (obj
== uleft
) || (obj
== uright
);
1088 switch (obj
->otyp
) {
1089 case RIN_GAIN_STRENGTH
:
1090 if ((obj
->owornmask
& W_RING
) && u_ring
) {
1095 case RIN_GAIN_CONSTITUTION
:
1096 if ((obj
->owornmask
& W_RING
) && u_ring
) {
1102 if ((obj
->owornmask
& W_RING
) && u_ring
) {
1107 case RIN_INCREASE_ACCURACY
:
1108 if ((obj
->owornmask
& W_RING
) && u_ring
)
1111 case RIN_INCREASE_DAMAGE
:
1112 if ((obj
->owornmask
& W_RING
) && u_ring
)
1115 case RIN_PROTECTION
:
1117 context
.botl
= 1; /* bot() will recalc u.uac */
1119 case HELM_OF_BRILLIANCE
:
1120 if ((obj
->owornmask
& W_ARMH
) && (obj
== uarmh
)) {
1126 case GAUNTLETS_OF_DEXTERITY
:
1127 if ((obj
->owornmask
& W_ARMG
) && (obj
== uarmg
)) {
1143 obj_resists(obj
, ochance
, achance
)
1145 int ochance
, achance
; /* percent chance for ordinary objects, artifacts */
1147 if (obj
->otyp
== AMULET_OF_YENDOR
1148 || obj
->otyp
== SPE_BOOK_OF_THE_DEAD
1149 || obj
->otyp
== CANDELABRUM_OF_INVOCATION
1150 || obj
->otyp
== BELL_OF_OPENING
1151 || (obj
->otyp
== CORPSE
&& is_rider(&mons
[obj
->corpsenm
]))) {
1154 int chance
= rn2(100);
1156 return (boolean
) (chance
< (obj
->oartifact
? achance
: ochance
));
1166 if (context
.bypasses
&& obj
->bypass
)
1169 if (obj
->oclass
== WAND_CLASS
)
1170 zap_odds
= 3; /* half-life = 2 zaps */
1171 else if (obj
->cursed
)
1172 zap_odds
= 3; /* half-life = 2 zaps */
1173 else if (obj
->blessed
)
1174 zap_odds
= 12; /* half-life = 8 zaps */
1176 zap_odds
= 8; /* half-life = 6 zaps */
1178 /* adjust for "large" quantities of identical things */
1182 return (boolean
) !rn2(zap_odds
);
1185 /* Use up at least minwt number of things made of material mat.
1186 * There's also a chance that other stuff will be used up. Finally,
1187 * there's a random factor here to keep from always using the stuff
1188 * at the top of the pile.
1191 polyuse(objhdr
, mat
, minwt
)
1195 register struct obj
*otmp
, *otmp2
;
1197 for (otmp
= objhdr
; minwt
> 0 && otmp
; otmp
= otmp2
) {
1198 otmp2
= otmp
->nexthere
;
1199 if (context
.bypasses
&& otmp
->bypass
)
1201 if (otmp
== uball
|| otmp
== uchain
)
1203 if (obj_resists(otmp
, 0, 0))
1204 continue; /* preserve unique objects */
1206 if (otmp
->otyp
== SCR_MAIL
)
1210 if (((int) objects
[otmp
->otyp
].oc_material
== mat
)
1211 == (rn2(minwt
+ 1) != 0)) {
1212 /* appropriately add damage to bill */
1213 if (costly_spot(otmp
->ox
, otmp
->oy
)) {
1215 addtobill(otmp
, FALSE
, FALSE
, FALSE
);
1217 (void) stolen_value(otmp
, otmp
->ox
, otmp
->oy
, FALSE
,
1220 if (otmp
->quan
< LARGEST_INT
)
1221 minwt
-= (int) otmp
->quan
;
1230 * Polymorph some of the stuff in this pile into a monster, preferably
1231 * a golem of the kind okind.
1234 create_polymon(obj
, okind
)
1238 struct permonst
*mdat
= (struct permonst
*) 0;
1240 const char *material
;
1243 if (context
.bypasses
) {
1244 /* this is approximate because the "no golems" !obj->nexthere
1245 check below doesn't understand bypassed objects; but it
1246 should suffice since bypassed objects always end up as a
1247 consecutive group at the top of their pile */
1248 while (obj
&& obj
->bypass
)
1249 obj
= obj
->nexthere
;
1252 /* no golems if you zap only one object -- not enough stuff */
1253 if (!obj
|| (!obj
->nexthere
&& obj
->quan
== 1L))
1256 /* some of these choices are arbitrary */
1261 pm_index
= PM_IRON_GOLEM
;
1262 material
= "metal ";
1269 pm_index
= rn2(2) ? PM_STONE_GOLEM
: PM_CLAY_GOLEM
;
1270 material
= "lithic ";
1274 /* there is no flesh type, but all food is type 0, so we use it */
1275 pm_index
= PM_FLESH_GOLEM
;
1276 material
= "organic ";
1279 pm_index
= PM_WOOD_GOLEM
;
1283 pm_index
= PM_LEATHER_GOLEM
;
1284 material
= "leather ";
1287 pm_index
= PM_ROPE_GOLEM
;
1288 material
= "cloth ";
1291 pm_index
= PM_SKELETON
; /* nearest thing to "bone golem" */
1295 pm_index
= PM_GOLD_GOLEM
;
1299 pm_index
= PM_GLASS_GOLEM
;
1300 material
= "glassy ";
1303 pm_index
= PM_PAPER_GOLEM
;
1304 material
= "paper ";
1307 /* if all else fails... */
1308 pm_index
= PM_STRAW_GOLEM
;
1313 if (!(mvitals
[pm_index
].mvflags
& G_GENOD
))
1314 mdat
= &mons
[pm_index
];
1316 mtmp
= makemon(mdat
, obj
->ox
, obj
->oy
, NO_MM_FLAGS
);
1317 polyuse(obj
, okind
, (int) mons
[pm_index
].cwt
);
1319 if (mtmp
&& cansee(mtmp
->mx
, mtmp
->my
)) {
1320 pline("Some %sobjects meld, and %s arises from the pile!", material
,
1325 /* Assumes obj is on the floor. */
1333 if (obj
->otyp
== SCR_MAIL
)
1338 if (poly_zapped
< 0) {
1339 /* some may metamorphosize */
1340 for (i
= obj
->quan
; i
; i
--)
1341 if (!rn2(Luck
+ 45)) {
1342 poly_zapped
= objects
[obj
->otyp
].oc_material
;
1347 /* if quan > 1 then some will survive intact */
1348 if (obj
->quan
> 1L) {
1349 if (obj
->quan
> LARGEST_INT
)
1350 obj
= splitobj(obj
, (long) rnd(30000));
1352 obj
= splitobj(obj
, (long) rnd((int) obj
->quan
- 1));
1355 /* appropriately add damage to bill */
1356 if (costly_spot(obj
->ox
, obj
->oy
)) {
1358 addtobill(obj
, FALSE
, FALSE
, FALSE
);
1360 (void) stolen_value(obj
, obj
->ox
, obj
->oy
, FALSE
, FALSE
);
1363 /* zap the object */
1367 /* classes of items whose current charge count carries over across polymorph
1369 static const char charged_objs
[] = { WAND_CLASS
, WEAPON_CLASS
, ARMOR_CLASS
,
1373 * Polymorph the object to the given object ID. If the ID is STRANGE_OBJECT
1374 * then pick random object from the source's class (this is the standard
1375 * "polymorph" case). If ID is set to a specific object, inhibit fusing
1376 * n objects into 1. This could have been added as a flag, but currently
1377 * it is tied to not being the standard polymorph case. The new polymorphed
1378 * object replaces obj in its link chains. Return value is a pointer to
1381 * This should be safe to call for an object anywhere.
1390 boolean can_merge
= (id
== STRANGE_OBJECT
);
1391 int obj_location
= obj
->where
;
1393 if (obj
->otyp
== BOULDER
)
1395 if (id
== STRANGE_OBJECT
) { /* preserve symbol */
1397 unsigned magic_obj
= objects
[obj
->otyp
].oc_magic
;
1399 if (obj
->otyp
== UNICORN_HORN
&& obj
->degraded_horn
)
1401 /* Try up to 3 times to make the magic-or-not status of
1402 the new item be the same as it was for the old one. */
1403 otmp
= (struct obj
*) 0;
1407 otmp
= mkobj(obj
->oclass
, FALSE
);
1408 } while (--try_limit
> 0
1409 && objects
[otmp
->otyp
].oc_magic
!= magic_obj
);
1411 /* literally replace obj with this new thing */
1412 otmp
= mksobj(id
, FALSE
, FALSE
);
1413 /* Actually more things use corpsenm but they polymorph differently */
1414 #define USES_CORPSENM(typ) \
1415 ((typ) == CORPSE || (typ) == STATUE || (typ) == FIGURINE)
1417 if (USES_CORPSENM(obj
->otyp
) && USES_CORPSENM(id
))
1418 set_corpsenm(otmp
, obj
->corpsenm
);
1419 #undef USES_CORPSENM
1422 /* preserve quantity */
1423 otmp
->quan
= obj
->quan
;
1424 /* preserve the shopkeepers (lack of) interest */
1425 otmp
->no_charge
= obj
->no_charge
;
1426 /* preserve inventory letter if in inventory */
1427 if (obj_location
== OBJ_INVENT
)
1428 otmp
->invlet
= obj
->invlet
;
1430 /* You can't send yourself 100 mail messages and then
1431 * polymorph them into useful scrolls
1433 if (obj
->otyp
== SCR_MAIL
) {
1434 otmp
->otyp
= SCR_MAIL
;
1439 /* avoid abusing eggs laid by you */
1440 if (obj
->otyp
== EGG
&& obj
->spe
) {
1441 int mnum
, tryct
= 100;
1443 /* first, turn into a generic egg */
1444 if (otmp
->otyp
== EGG
)
1448 otmp
->owt
= weight(otmp
);
1450 otmp
->corpsenm
= NON_PM
;
1453 /* now change it into something laid by the hero */
1455 mnum
= can_be_hatched(random_monster());
1456 if (mnum
!= NON_PM
&& !dead_species(mnum
, TRUE
)) {
1457 otmp
->spe
= 1; /* laid by hero */
1458 set_corpsenm(otmp
, mnum
); /* also sets hatch timer */
1464 /* keep special fields (including charges on wands) */
1465 if (index(charged_objs
, otmp
->oclass
))
1466 otmp
->spe
= obj
->spe
;
1467 otmp
->recharged
= obj
->recharged
;
1469 otmp
->cursed
= obj
->cursed
;
1470 otmp
->blessed
= obj
->blessed
;
1472 if (erosion_matters(otmp
)) {
1473 if (is_flammable(otmp
) || is_rustprone(otmp
))
1474 otmp
->oeroded
= obj
->oeroded
;
1475 if (is_corrodeable(otmp
) || is_rottable(otmp
))
1476 otmp
->oeroded2
= obj
->oeroded2
;
1477 if (is_damageable(otmp
))
1478 otmp
->oerodeproof
= obj
->oerodeproof
;
1481 /* Keep chest/box traps and poisoned ammo if we may */
1482 if (obj
->otrapped
&& Is_box(otmp
))
1483 otmp
->otrapped
= TRUE
;
1485 if (obj
->opoisoned
&& is_poisonable(otmp
))
1486 otmp
->opoisoned
= TRUE
;
1488 if (id
== STRANGE_OBJECT
&& obj
->otyp
== CORPSE
) {
1489 /* turn crocodile corpses into shoes */
1490 if (obj
->corpsenm
== PM_CROCODILE
) {
1491 otmp
->otyp
= LOW_BOOTS
;
1492 otmp
->oclass
= ARMOR_CLASS
;
1495 otmp
->oerodeproof
= TRUE
;
1497 otmp
->cursed
= FALSE
;
1501 /* no box contents --KAA */
1502 if (Has_contents(otmp
))
1503 delete_contents(otmp
);
1505 /* 'n' merged objects may be fused into 1 object */
1506 if (otmp
->quan
> 1L && (!objects
[otmp
->otyp
].oc_merge
1507 || (can_merge
&& otmp
->quan
> (long) rn2(1000))))
1510 switch (otmp
->oclass
) {
1512 if (otmp
->otyp
== MAGIC_LAMP
) {
1513 otmp
->otyp
= OIL_LAMP
;
1514 otmp
->age
= 1500L; /* "best" oil lamp possible */
1515 } else if (otmp
->otyp
== MAGIC_MARKER
) {
1516 otmp
->recharged
= 1; /* degraded quality */
1518 /* don't care about the recharge count of other tools */
1522 while (otmp
->otyp
== WAN_WISHING
|| otmp
->otyp
== WAN_POLYMORPH
)
1523 otmp
->otyp
= rnd_class(WAN_LIGHT
, WAN_LIGHTNING
);
1524 /* altering the object tends to degrade its quality
1525 (analogous to spellbook `read count' handling) */
1526 if ((int) otmp
->recharged
< rn2(7)) /* recharge_limit */
1531 while (otmp
->otyp
== POT_POLYMORPH
)
1532 otmp
->otyp
= rnd_class(POT_GAIN_ABILITY
, POT_WATER
);
1536 while (otmp
->otyp
== SPE_POLYMORPH
)
1537 otmp
->otyp
= rnd_class(SPE_DIG
, SPE_BLANK_PAPER
);
1538 /* reduce spellbook abuse; non-blank books degrade */
1539 if (otmp
->otyp
!= SPE_BLANK_PAPER
) {
1540 otmp
->spestudied
= obj
->spestudied
+ 1;
1541 if (otmp
->spestudied
> MAX_SPELL_STUDY
) {
1542 otmp
->otyp
= SPE_BLANK_PAPER
;
1543 /* writing a new book over it will yield an unstudied
1544 one; re-polymorphing this one as-is may or may not
1545 get something non-blank */
1546 otmp
->spestudied
= rn2(otmp
->spestudied
);
1552 if (otmp
->quan
> (long) rnd(4)
1553 && objects
[obj
->otyp
].oc_material
== MINERAL
1554 && objects
[otmp
->otyp
].oc_material
!= MINERAL
) {
1555 otmp
->otyp
= ROCK
; /* transmutation backfired */
1556 otmp
->quan
/= 2L; /* some material has been lost */
1561 /* update the weight */
1562 otmp
->owt
= weight(otmp
);
1564 /* handle polymorph of worn item: stone-to-flesh cast on self can
1565 affect multiple objects at once, but their new forms won't
1566 produce any side-effects; a single worn item dipped into potion
1567 of polymorph can produce side-effects but those won't yield out
1568 of sequence messages because current polymorph is finished */
1569 if (obj_location
== OBJ_INVENT
&& obj
->owornmask
) {
1570 long old_wornmask
= obj
->owornmask
& ~(W_ART
| W_ARTI
),
1571 new_wornmask
= wearslot(otmp
);
1572 boolean was_twohanded
= bimanual(obj
), was_twoweap
= u
.twoweap
;
1574 remove_worn_item(obj
, TRUE
);
1575 /* if the new form can be worn in the same slot, make it so
1576 [possible extension: if it could be worn in some other
1577 slot which is currently unfilled, wear it there instead] */
1578 if ((old_wornmask
& W_QUIVER
) != 0L) {
1580 } else if ((old_wornmask
& W_SWAPWEP
) != 0L) {
1581 if (was_twohanded
|| !bimanual(otmp
))
1583 if (was_twoweap
&& uswapwep
)
1585 } else if ((old_wornmask
& W_WEP
) != 0L) {
1586 if (was_twohanded
|| !bimanual(otmp
) || !uarms
)
1588 if (was_twoweap
&& uwep
&& !bimanual(uwep
))
1590 } else if ((old_wornmask
& new_wornmask
) != 0L) {
1591 new_wornmask
&= old_wornmask
;
1592 setworn(otmp
, new_wornmask
);
1593 set_wear(otmp
); /* Armor_on() for side-effects */
1597 /* ** we are now done adjusting the object ** */
1599 /* swap otmp for obj */
1600 replace_object(obj
, otmp
);
1601 if (obj_location
== OBJ_INVENT
) {
1603 * We may need to do extra adjustments for the hero if we're
1604 * messing with the hero's inventory. The following calls are
1605 * equivalent to calling freeinv on obj and addinv on otmp,
1606 * while doing an in-place swap of the actual objects.
1611 } else if (obj_location
== OBJ_FLOOR
) {
1612 ox
= otmp
->ox
, oy
= otmp
->oy
; /* set by replace_object() */
1613 if (obj
->otyp
== BOULDER
&& otmp
->otyp
!= BOULDER
1614 && !does_block(ox
, oy
, &levl
[ox
][oy
]))
1615 unblock_point(ox
, oy
);
1616 else if (obj
->otyp
!= BOULDER
&& otmp
->otyp
== BOULDER
)
1617 /* (checking does_block() here would be redundant) */
1618 block_point(ox
, oy
);
1621 if ((!carried(otmp
) || obj
->unpaid
)
1622 && get_obj_location(otmp
, &ox
, &oy
, BURIED_TOO
| CONTAINED_TOO
)
1623 && costly_spot(ox
, oy
)) {
1624 register struct monst
*shkp
=
1625 shop_keeper(*in_rooms(ox
, oy
, SHOPBASE
));
1627 if ((!obj
->no_charge
1628 || (Has_contents(obj
)
1629 && (contained_cost(obj
, shkp
, 0L, FALSE
, FALSE
) != 0L)))
1630 && inhishop(shkp
)) {
1631 if (shkp
->mpeaceful
) {
1633 && *in_rooms(u
.ux
, u
.uy
, 0)
1634 == *in_rooms(shkp
->mx
, shkp
->my
, 0)
1635 && !costly_spot(u
.ux
, u
.uy
)) {
1636 make_angry_shk(shkp
, ox
, oy
);
1638 pline("%s gets angry!", Monnam(shkp
));
1642 Norep("%s is furious!", Monnam(shkp
));
1649 /* stone-to-flesh spell hits and maybe transforms or animates obj */
1651 stone_to_flesh_obj(obj
)
1654 int res
= 1; /* affected object by default */
1655 struct permonst
*ptr
;
1656 struct monst
*mon
, *shkp
;
1659 boolean smell
= FALSE
, golem_xform
= FALSE
;
1661 if (objects
[obj
->otyp
].oc_material
!= MINERAL
1662 && objects
[obj
->otyp
].oc_material
!= GEMSTONE
)
1664 /* Heart of Ahriman usually resists; ordinary items rarely do */
1665 if (obj_resists(obj
, 2, 98))
1668 (void) get_obj_location(obj
, &oox
, &ooy
, 0);
1669 /* add more if stone objects are added.. */
1670 switch (objects
[obj
->otyp
].oc_class
) {
1671 case ROCK_CLASS
: /* boulders and statues */
1672 case TOOL_CLASS
: /* figurines */
1673 if (obj
->otyp
== BOULDER
) {
1674 obj
= poly_obj(obj
, HUGE_CHUNK_OF_MEAT
);
1676 } else if (obj
->otyp
== STATUE
|| obj
->otyp
== FIGURINE
) {
1677 ptr
= &mons
[obj
->corpsenm
];
1678 if (is_golem(ptr
)) {
1679 golem_xform
= (ptr
!= &mons
[PM_FLESH_GOLEM
]);
1680 } else if (vegetarian(ptr
)) {
1681 /* Don't animate monsters that aren't flesh */
1682 obj
= poly_obj(obj
, MEATBALL
);
1686 if (obj
->otyp
== STATUE
) {
1687 /* animate_statue() forces all golems to become flesh golems */
1688 mon
= animate_statue(obj
, oox
, ooy
, ANIMATE_SPELL
, (int *) 0);
1689 } else { /* (obj->otyp == FIGURINE) */
1691 ptr
= &mons
[PM_FLESH_GOLEM
];
1692 mon
= makemon(ptr
, oox
, ooy
, NO_MINVENT
);
1694 if (costly_spot(oox
, ooy
)
1695 && (carried(obj
) ? obj
->unpaid
: !obj
->no_charge
)) {
1696 shkp
= shop_keeper(*in_rooms(oox
, ooy
, SHOPBASE
));
1697 stolen_value(obj
, oox
, ooy
,
1698 (shkp
&& shkp
->mpeaceful
), FALSE
);
1701 obj_stop_timers(obj
);
1706 if (cansee(mon
->mx
, mon
->my
))
1707 pline_The("figurine %sanimates!",
1708 golem_xform
? "turns to flesh and " : "");
1713 /* this golem handling is redundant... */
1714 if (is_golem(ptr
) && ptr
!= &mons
[PM_FLESH_GOLEM
])
1715 (void) newcham(mon
, &mons
[PM_FLESH_GOLEM
], TRUE
, FALSE
);
1716 } else if ((ptr
->geno
& (G_NOCORPSE
| G_UNIQ
)) != 0) {
1717 /* didn't revive but can't leave corpse either */
1720 /* unlikely to get here since genociding monsters also
1721 sets the G_NOCORPSE flag; drop statue's contents */
1722 while ((item
= obj
->cobj
) != 0) {
1723 bypass_obj(item
); /* make stone-to-flesh miss it */
1724 obj_extract_self(item
);
1725 place_object(item
, oox
, ooy
);
1727 obj
= poly_obj(obj
, CORPSE
);
1729 } else { /* miscellaneous tool or unexpected rock... */
1733 /* maybe add weird things to become? */
1734 case RING_CLASS
: /* some of the rings are stone */
1735 obj
= poly_obj(obj
, MEAT_RING
);
1738 case WAND_CLASS
: /* marble wand */
1739 obj
= poly_obj(obj
, MEAT_STICK
);
1742 case GEM_CLASS
: /* stones & gems */
1743 obj
= poly_obj(obj
, MEATBALL
);
1746 case WEAPON_CLASS
: /* crysknife */
1754 /* non-meat eaters smell meat, meat eaters smell its flavor;
1755 monks are considered non-meat eaters regardless of behavior;
1756 other roles are non-meat eaters if they haven't broken
1757 vegetarian conduct yet (or if poly'd into non-carnivorous/
1758 non-omnivorous form, regardless of whether it's herbivorous,
1759 non-eating, or something stranger) */
1760 if (Role_if(PM_MONK
) || !u
.uconduct
.unvegetarian
1761 || !carnivorous(youmonst
.data
))
1762 Norep("You smell the odor of meat.");
1764 Norep("You smell a delicious smell.");
1771 * Object obj was hit by the effect of the wand/spell otmp. Return
1772 * non-zero if the wand/spell had any effect.
1776 struct obj
*obj
, *otmp
;
1778 int res
= 1; /* affected object by default */
1779 boolean learn_it
= FALSE
, maybelearnit
;
1781 /* fundamental: a wand effect hitting itself doesn't do anything;
1782 otherwise we need to guard against accessing otmp after something
1783 strange has happened to it (along the lines of polymorph or
1784 stone-to-flesh [which aren't good examples since polymorph wands
1785 aren't affected by polymorph zaps and stone-to-flesh isn't
1786 available in wand form, but the concept still applies...]) */
1791 /* The bypass bit is currently only used as follows:
1793 * POLYMORPH - When a monster being polymorphed drops something
1794 * from its inventory as a result of the change.
1795 * If the items fall to the floor, they are not
1796 * subject to direct subsequent polymorphing
1797 * themselves on that same zap. This makes it
1798 * consistent with items that remain in the
1799 * monster's inventory. They are not polymorphed
1801 * UNDEAD_TURNING - When an undead creature gets killed via
1802 * undead turning, prevent its corpse from being
1803 * immediately revived by the same effect.
1804 * STONE_TO_FLESH - If a statue can't be revived, its
1805 * contents get dropped before turning it into
1806 * meat; prevent those contents from being hit.
1807 * retouch_equipment() - bypass flag is used to track which
1808 * items have been handled (bhito isn't involved).
1809 * menu_drop(), askchain() - inventory traversal where multiple
1810 * Drop can alter the invent chain while traversal
1811 * is in progress (bhito isn't involved).
1813 * The bypass bit on all objects is reset each turn, whenever
1814 * context.bypasses is set.
1816 * We check the obj->bypass bit above AND context.bypasses
1817 * as a safeguard against any stray occurrence left in an obj
1818 * struct someplace, although that should never happen.
1820 if (context
.bypasses
) {
1823 debugpline1("%s for a moment.", Tobjnam(obj
, "pulsate"));
1829 * Some parts of this function expect the object to be on the floor
1830 * obj->{ox,oy} to be valid. The exception to this (so far) is
1831 * for the STONE_TO_FLESH spell.
1833 if (!(obj
->where
== OBJ_FLOOR
|| otmp
->otyp
== SPE_STONE_TO_FLESH
))
1834 impossible("bhito: obj is not floor or Stone To Flesh spell");
1838 } else if (obj
== uchain
) {
1839 if (otmp
->otyp
== WAN_OPENING
|| otmp
->otyp
== SPE_KNOCK
) {
1845 switch (otmp
->otyp
) {
1848 if (obj
->otyp
== WAN_POLYMORPH
|| obj
->otyp
== SPE_POLYMORPH
1849 || obj
->otyp
== POT_POLYMORPH
|| obj_resists(obj
, 5, 95)) {
1854 u
.uconduct
.polypiles
++;
1855 /* any saved lock context will be dangerously obsolete */
1857 (void) boxlock(obj
, otmp
);
1859 if (obj_shudders(obj
)) {
1861 ((obj
== level
.objects
[u
.ux
][u
.uy
]) && u
.uundetected
1862 && hides_under(youmonst
.data
));
1864 if (cansee(obj
->ox
, obj
->oy
))
1867 /* eek - your cover might have been blown */
1869 (void) hideunder(&youmonst
);
1872 obj
= poly_obj(obj
, STRANGE_OBJECT
);
1873 newsym(obj
->ox
, obj
->oy
);
1877 /* target object has now been "seen (up close)" */
1879 if (Is_container(obj
) || obj
->otyp
== STATUE
) {
1880 obj
->cknown
= obj
->lknown
= 1;
1882 boolean catbox
= SchroedingersBox(obj
);
1884 /* we don't want to force alive vs dead
1885 determination for Schroedinger's Cat here,
1886 so just make probing be inconclusive for it */
1889 pline("%s empty.", Tobjnam(obj
, catbox
? "seem" : "are"));
1892 /* view contents (not recursively) */
1893 for (o
= obj
->cobj
; o
; o
= o
->nobj
)
1894 o
->dknown
= 1; /* "seen", even if blind */
1895 (void) display_cinventory(obj
);
1903 case SPE_FORCE_BOLT
:
1904 /* learn the type if you see or hear something break
1905 (the sound could be implicit) */
1906 maybelearnit
= cansee(obj
->ox
, obj
->oy
) || !Deaf
;
1907 if (obj
->otyp
== BOULDER
) {
1908 if (cansee(obj
->ox
, obj
->oy
))
1909 pline_The("boulder falls apart.");
1911 You_hear("a crumbling sound.");
1913 } else if (obj
->otyp
== STATUE
) {
1914 if (break_statue(obj
)) {
1915 if (cansee(obj
->ox
, obj
->oy
)) {
1917 pline_The("%s shatters.", rndmonnam(NULL
));
1919 pline_The("statue shatters.");
1921 You_hear("a crumbling sound.");
1926 if (context
.mon_moving
1927 ? !breaks(obj
, obj
->ox
, obj
->oy
)
1928 : !hero_breaks(obj
, obj
->ox
, obj
->oy
, FALSE
))
1929 maybelearnit
= FALSE
; /* nothing broke */
1931 newsym_force(oox
,ooy
);
1937 case WAN_CANCELLATION
:
1938 case SPE_CANCELLATION
:
1941 newsym(obj
->ox
, obj
->oy
); /* might change color */
1944 case SPE_DRAIN_LIFE
:
1945 (void) drain_item(obj
, TRUE
);
1947 case WAN_TELEPORTATION
:
1948 case SPE_TELEPORT_AWAY
:
1951 case WAN_MAKE_INVISIBLE
:
1953 case WAN_UNDEAD_TURNING
:
1954 case SPE_TURN_UNDEAD
:
1955 if (obj
->otyp
== EGG
) {
1957 } else if (obj
->otyp
== CORPSE
) {
1958 int corpsenm
= corpse_revive_type(obj
);
1960 res
= !!revive(obj
, TRUE
);
1961 if (res
&& Role_if(PM_HEALER
)) {
1962 if (Hallucination
&& !Deaf
) {
1963 You_hear("the sound of a defibrillator.");
1965 } else if (!Blind
) {
1966 You("observe %s %s change dramatically.",
1967 s_suffix(an(mons
[corpsenm
].mname
)),
1968 nonliving(&mons
[corpsenm
]) ? "motility"
1973 exercise(A_WIS
, TRUE
);
1980 case SPE_WIZARD_LOCK
:
1982 res
= boxlock(obj
, otmp
);
1988 case WAN_SLOW_MONSTER
: /* no effect on objects */
1989 case SPE_SLOW_MONSTER
:
1990 case WAN_SPEED_MONSTER
:
1993 case SPE_EXTRA_HEALING
:
1996 case SPE_STONE_TO_FLESH
:
1997 res
= stone_to_flesh_obj(obj
);
2000 impossible("What an interesting effect (%d)", otmp
->otyp
);
2003 /* if effect was observable then discover the wand type provided
2004 that the wand itself has been seen */
2010 /* returns nonzero if something was hit */
2012 bhitpile(obj
, fhito
, tx
, ty
, zz
)
2014 int FDECL((*fhito
), (OBJ_P
, OBJ_P
));
2018 int hitanything
= 0;
2019 register struct obj
*otmp
, *next_obj
;
2021 if (obj
->otyp
== SPE_FORCE_BOLT
|| obj
->otyp
== WAN_STRIKING
) {
2022 struct trap
*t
= t_at(tx
, ty
);
2024 /* We can't settle for the default calling sequence of
2025 bhito(otmp) -> break_statue(otmp) -> activate_statue_trap(ox,oy)
2026 because that last call might end up operating on our `next_obj'
2027 (below), rather than on the current object, if it happens to
2028 encounter a statue which mustn't become animated. */
2029 if (t
&& t
->ttyp
== STATUE_TRAP
2030 && activate_statue_trap(t
, tx
, ty
, TRUE
))
2035 for (otmp
= level
.objects
[tx
][ty
]; otmp
; otmp
= next_obj
) {
2036 next_obj
= otmp
->nexthere
;
2037 /* for zap downwards, don't hit object poly'd hero is hiding under */
2038 if (zz
> 0 && u
.uundetected
&& otmp
== level
.objects
[u
.ux
][u
.uy
]
2039 && hides_under(youmonst
.data
))
2042 hitanything
+= (*fhito
)(otmp
, obj
);
2044 if (poly_zapped
>= 0)
2045 create_polymon(level
.objects
[tx
][ty
], poly_zapped
);
2051 * zappable - returns 1 if zap is available, 0 otherwise.
2052 * it removes a charge from the wand if zappable.
2053 * added by GAN 11/03/86
2057 register struct obj
*wand
;
2059 if (wand
->spe
< 0 || (wand
->spe
== 0 && rn2(121)))
2062 You("wrest one last charge from the worn-out wand.");
2068 * zapnodir - zaps a NODIR wand/spell.
2069 * added by GAN 11/03/86
2073 register struct obj
*obj
;
2075 boolean known
= FALSE
;
2077 switch (obj
->otyp
) {
2083 if (lightdamage(obj
, TRUE
, 5))
2086 case WAN_SECRET_DOOR_DETECTION
:
2087 case SPE_DETECT_UNSEEN
:
2093 case WAN_CREATE_MONSTER
:
2094 known
= create_critters(rn2(23) ? 1 : rn1(7, 2),
2095 (struct permonst
*) 0, FALSE
);
2099 if (Luck
+ rn2(5) < 0) {
2100 pline("Unfortunately, nothing happens.");
2105 case WAN_ENLIGHTENMENT
:
2107 You_feel("self-knowledgeable...");
2108 display_nhwindow(WIN_MESSAGE
, FALSE
);
2109 enlightenment(MAGICENLIGHTENMENT
, ENL_GAMEINPROGRESS
);
2110 pline_The("feeling subsides.");
2111 exercise(A_WIS
, TRUE
);
2115 if (!objects
[obj
->otyp
].oc_name_known
)
2116 more_experienced(0, 10);
2117 /* effect was observable; discover the wand type provided
2118 that the wand itself has been seen */
2128 otmp
->in_use
= TRUE
; /* in case losehp() is fatal */
2129 pline("%s suddenly explodes!", The(xname(otmp
)));
2130 dmg
= d(otmp
->spe
+ 2, 6);
2131 losehp(Maybe_Half_Phys(dmg
), "exploding wand", KILLED_BY_AN
);
2135 static NEARDATA
const char zap_syms
[] = { WAND_CLASS
, 0 };
2137 /* 'z' command (or 'y' if numbed_pad==-1) */
2141 register struct obj
*obj
;
2144 if (check_capacity((char *) 0))
2146 obj
= getobj(zap_syms
, "zap");
2152 /* zappable addition done by GAN 11/03/86 */
2154 pline1(nothing_happens
);
2155 else if (obj
->cursed
&& !rn2(WAND_BACKFIRE_CHANCE
)) {
2156 backfire(obj
); /* the wand blows up in your face! */
2157 exercise(A_STR
, FALSE
);
2159 } else if (!(objects
[obj
->otyp
].oc_dir
== NODIR
) && !getdir((char *) 0)) {
2161 pline("%s glows and fades.", The(xname(obj
)));
2162 /* make him pay for knowing !NODIR */
2163 } else if (!u
.dx
&& !u
.dy
&& !u
.dz
2164 && !(objects
[obj
->otyp
].oc_dir
== NODIR
)) {
2165 if ((damage
= zapyourself(obj
, TRUE
)) != 0) {
2168 Sprintf(buf
, "zapped %sself with a wand", uhim());
2169 losehp(Maybe_Half_Phys(damage
), buf
, NO_KILLER_PREFIX
);
2172 /* Are we having fun yet?
2173 * weffects -> buzz(obj->otyp) -> zhitm (temple priest) ->
2174 * attack -> hitum -> known_hitum -> ghod_hitsu ->
2175 * buzz(AD_ELEC) -> destroy_item(WAND_CLASS) ->
2176 * useup -> obfree -> dealloc_obj -> free(obj)
2183 if (obj
&& obj
->spe
< 0) {
2184 pline("%s to dust.", Tobjnam(obj
, "turn"));
2187 update_inventory(); /* maybe used a charge */
2192 zapyourself(obj
, ordinary
)
2196 boolean learn_it
= FALSE
;
2199 switch (obj
->otyp
) {
2201 case SPE_FORCE_BOLT
:
2204 shieldeff(u
.ux
, u
.uy
);
2208 You("bash yourself!");
2211 damage
= d(1 + obj
->spe
, 6);
2212 exercise(A_STR
, FALSE
);
2218 if (!Shock_resistance
) {
2219 You("shock yourself!");
2221 exercise(A_CON
, FALSE
);
2223 shieldeff(u
.ux
, u
.uy
);
2224 You("zap yourself, but seem unharmed.");
2225 ugolemeffects(AD_ELEC
, d(12, 6));
2227 destroy_item(WAND_CLASS
, AD_ELEC
);
2228 destroy_item(RING_CLASS
, AD_ELEC
);
2229 (void) flashburn((long) rnd(100));
2233 You("explode a fireball on top of yourself!");
2234 explode(u
.ux
, u
.uy
, 11, d(6, 6), WAND_CLASS
, EXPL_FIERY
);
2239 if (Fire_resistance
) {
2240 shieldeff(u
.ux
, u
.uy
);
2241 You_feel("rather warm.");
2242 ugolemeffects(AD_FIRE
, d(12, 6));
2244 pline("You've set yourself afire!");
2248 (void) burnarmor(&youmonst
);
2249 destroy_item(SCROLL_CLASS
, AD_FIRE
);
2250 destroy_item(POTION_CLASS
, AD_FIRE
);
2251 destroy_item(SPBOOK_CLASS
, AD_FIRE
);
2252 destroy_item(FOOD_CLASS
, AD_FIRE
); /* only slime for now */
2256 case SPE_CONE_OF_COLD
:
2259 if (Cold_resistance
) {
2260 shieldeff(u
.ux
, u
.uy
);
2261 You_feel("a little chill.");
2262 ugolemeffects(AD_COLD
, d(12, 6));
2264 You("imitate a popsicle!");
2267 destroy_item(POTION_CLASS
, AD_COLD
);
2270 case WAN_MAGIC_MISSILE
:
2271 case SPE_MAGIC_MISSILE
:
2274 shieldeff(u
.ux
, u
.uy
);
2275 pline_The("missiles bounce!");
2278 pline("Idiot! You've shot yourself!");
2290 case WAN_CANCELLATION
:
2291 case SPE_CANCELLATION
:
2292 (void) cancel_monst(&youmonst
, obj
, TRUE
, FALSE
, TRUE
);
2295 case SPE_DRAIN_LIFE
:
2296 if (!Drain_resistance
) {
2297 learn_it
= TRUE
; /* (no effect for spells...) */
2298 losexp("life drainage");
2300 damage
= 0; /* No additional damage */
2303 case WAN_MAKE_INVISIBLE
: {
2304 /* have to test before changing HInvis but must change
2305 * HInvis before doing newsym().
2307 int msg
= !Invis
&& !Blind
&& !BInvis
;
2309 if (BInvis
&& uarmc
->otyp
== MUMMY_WRAPPING
) {
2310 /* A mummy wrapping absorbs it and protects you */
2311 You_feel("rather itchy under %s.", yname(uarmc
));
2314 if (ordinary
|| !rn2(10)) { /* permanent */
2315 HInvis
|= FROMOUTSIDE
;
2316 } else { /* temporary */
2317 incr_itimeout(&HInvis
, d(obj
->spe
, 250));
2322 self_invis_message();
2327 case WAN_SPEED_MONSTER
:
2328 if (!(HFast
& INTRINSIC
)) {
2333 Your("quickness feels more natural.");
2334 exercise(A_DEX
, TRUE
);
2336 HFast
|= FROMOUTSIDE
;
2342 if (Sleep_resistance
) {
2343 shieldeff(u
.ux
, u
.uy
);
2344 You("don't feel sleepy!");
2346 pline_The("sleep ray hits you!");
2347 fall_asleep(-rnd(50), TRUE
);
2351 case WAN_SLOW_MONSTER
:
2352 case SPE_SLOW_MONSTER
:
2353 if (HFast
& (TIMEOUT
| INTRINSIC
)) {
2359 case WAN_TELEPORTATION
:
2360 case SPE_TELEPORT_AWAY
:
2362 /* same criteria as when mounted (zap_steed) */
2363 if ((Teleport_control
&& !Stunned
) || !couldsee(u
.ux0
, u
.uy0
)
2364 || distu(u
.ux0
, u
.uy0
) >= 16)
2369 case SPE_FINGER_OF_DEATH
:
2370 if (nonliving(youmonst
.data
) || is_demon(youmonst
.data
)) {
2371 pline((obj
->otyp
== WAN_DEATH
)
2372 ? "The wand shoots an apparently harmless beam at you."
2373 : "You seem no deader than before.");
2377 Sprintf(killer
.name
, "shot %sself with a death ray", uhim());
2378 killer
.format
= NO_KILLER_PREFIX
;
2379 You("irradiate yourself with pure energy!");
2381 /* They might survive with an amulet of life saving */
2384 case WAN_UNDEAD_TURNING
:
2385 case SPE_TURN_UNDEAD
:
2387 (void) unturn_dead(&youmonst
);
2388 if (is_undead(youmonst
.data
)) {
2389 You_feel("frightened and %sstunned.",
2390 Stunned
? "even more " : "");
2391 make_stunned((HStun
& TIMEOUT
) + (long) rnd(30), FALSE
);
2393 You("shudder in dread.");
2396 case SPE_EXTRA_HEALING
:
2397 learn_it
= TRUE
; /* (no effect for spells...) */
2398 healup(d(6, obj
->otyp
== SPE_EXTRA_HEALING
? 8 : 4), 0, FALSE
,
2399 (obj
->otyp
== SPE_EXTRA_HEALING
));
2400 You_feel("%sbetter.", obj
->otyp
== SPE_EXTRA_HEALING
? "much " : "");
2402 case WAN_LIGHT
: /* (broken wand) */
2403 /* assert( !ordinary ); */
2404 damage
= d(obj
->spe
, 25);
2405 case EXPENSIVE_CAMERA
:
2408 damage
= lightdamage(obj
, ordinary
, damage
);
2410 if (flashburn((long) damage
))
2412 damage
= 0; /* reset */
2420 if (u
.utrap
) { /* escape web or bear trap */
2421 (void) openholdingtrap(&youmonst
, &learn_it
);
2424 /* unlock carried boxes */
2425 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
)
2427 (void) boxlock(otmp
, obj
);
2428 /* trigger previously escaped trapdoor */
2429 (void) openfallingtrap(&youmonst
, TRUE
, &learn_it
);
2433 case SPE_WIZARD_LOCK
:
2435 (void) closeholdingtrap(&youmonst
, &learn_it
);
2440 case SPE_DETECT_UNSEEN
:
2446 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
) {
2448 if (Is_container(otmp
) || otmp
->otyp
== STATUE
) {
2450 if (!SchroedingersBox(otmp
))
2458 case SPE_STONE_TO_FLESH
: {
2459 struct obj
*otmp
, *onxt
;
2462 if (u
.umonnum
== PM_STONE_GOLEM
) {
2464 (void) polymon(PM_FLESH_GOLEM
);
2468 fix_petrification(); /* saved! */
2470 /* but at a cost.. */
2471 for (otmp
= invent
; otmp
; otmp
= onxt
) {
2473 if (bhito(otmp
, obj
))
2477 * It is possible that we can now merge some inventory.
2478 * Do a highly paranoid merge. Restart from the beginning
2483 for (otmp
= invent
; !didmerge
&& otmp
; otmp
= otmp
->nobj
)
2484 for (onxt
= otmp
->nobj
; onxt
; onxt
= onxt
->nobj
)
2485 if (merged(&otmp
, &onxt
)) {
2493 impossible("zapyourself: object %d used?", obj
->otyp
);
2496 /* if effect was observable then discover the wand type provided
2497 that the wand itself has been seen */
2503 /* called when poly'd hero uses breath attack against self */
2506 struct attack
*mattk
;
2508 int dtyp
= 20 + mattk
->adtyp
- 1; /* breath by hero */
2509 const char *fltxt
= flash_types
[dtyp
]; /* blast of <something> */
2511 zhitu(dtyp
, mattk
->damn
, fltxt
, u
.ux
, u
.uy
);
2514 /* light damages hero in gremlin form */
2516 lightdamage(obj
, ordinary
, amt
)
2517 struct obj
*obj
; /* item making light (fake book if spell) */
2518 boolean ordinary
; /* wand/camera zap vs wand destruction */
2519 int amt
; /* pseudo-damage used to determine blindness duration */
2525 if (dmg
&& youmonst
.data
== &mons
[PM_GREMLIN
]) {
2526 /* reduce high values (from destruction of wand with many charges) */
2529 dmg
= 10 + rnd(dmg
- 10);
2532 pline("Ow, that light hurts%c", (dmg
> 2 || u
.mh
<= 5) ? '!' : '.');
2533 /* [composing killer/reason is superfluous here; if fatal, cause
2534 of death will always be "killed while stuck in creature form"] */
2535 if (obj
->oclass
== SCROLL_CLASS
|| obj
->oclass
== SPBOOK_CLASS
)
2536 ordinary
= FALSE
; /* say blasted rather than zapped */
2537 how
= (obj
->oclass
!= SPBOOK_CLASS
)
2538 ? (const char *) ansimpleoname(obj
)
2540 Sprintf(buf
, "%s %sself with %s", ordinary
? "zapped" : "blasted",
2542 /* might rehumanize(); could be fatal, but only for Unchanging */
2543 losehp(Maybe_Half_Phys(dmg
), buf
, NO_KILLER_PREFIX
);
2548 /* light[ning] causes blindness */
2553 if (!resists_blnd(&youmonst
)) {
2554 You(are_blinded_by_the_flash
);
2555 make_blinded(duration
, FALSE
);
2557 Your1(vision_clears
);
2563 /* you've zapped a wand downwards while riding
2564 * Return TRUE if the steed was hit by the wand.
2565 * Return FALSE if the steed was not hit by the wand.
2569 struct obj
*obj
; /* wand or spell */
2571 int steedhit
= FALSE
;
2573 bhitpos
.x
= u
.usteed
->mx
, bhitpos
.y
= u
.usteed
->my
;
2575 switch (obj
->otyp
) {
2577 * Wands that are allowed to hit the steed
2578 * Carefully test the results of any that are
2579 * moved here from the bottom section.
2582 probe_monster(u
.usteed
);
2586 case WAN_TELEPORTATION
:
2587 case SPE_TELEPORT_AWAY
:
2588 /* you go together */
2590 /* same criteria as when unmounted (zapyourself) */
2591 if ((Teleport_control
&& !Stunned
) || !couldsee(u
.ux0
, u
.uy0
)
2592 || distu(u
.ux0
, u
.uy0
) >= 16)
2597 /* Default processing via bhitm() for these */
2598 case SPE_CURE_SICKNESS
:
2599 case WAN_MAKE_INVISIBLE
:
2600 case WAN_CANCELLATION
:
2601 case SPE_CANCELLATION
:
2605 case SPE_FORCE_BOLT
:
2606 case WAN_SLOW_MONSTER
:
2607 case SPE_SLOW_MONSTER
:
2608 case WAN_SPEED_MONSTER
:
2610 case SPE_EXTRA_HEALING
:
2611 case SPE_DRAIN_LIFE
:
2614 (void) bhitm(u
.usteed
, obj
);
2626 * cancel a monster (possibly the hero). inventory is cancelled only
2627 * if the monster is zapping itself directly, since otherwise the
2628 * effect is too strong. currently non-hero monsters do not zap
2629 * themselves with cancellation.
2632 cancel_monst(mdef
, obj
, youattack
, allow_cancel_kill
, self_cancel
)
2633 register struct monst
*mdef
;
2634 register struct obj
*obj
;
2635 boolean youattack
, allow_cancel_kill
, self_cancel
;
2637 boolean youdefend
= (mdef
== &youmonst
);
2638 static const char writing_vanishes
[] =
2639 "Some writing vanishes from %s head!";
2640 static const char your
[] = "your"; /* should be extern */
2642 if (youdefend
? (!youattack
&& Antimagic
)
2643 : resist(mdef
, obj
->oclass
, 0, NOTELL
))
2644 return FALSE
; /* resisted cancellation */
2646 if (self_cancel
) { /* 1st cancel inventory */
2649 for (otmp
= (youdefend
? invent
: mdef
->minvent
); otmp
;
2653 context
.botl
= 1; /* potential AC change */
2658 /* now handle special cases */
2661 if ((u
.umonnum
== PM_CLAY_GOLEM
) && !Blind
)
2662 pline(writing_vanishes
, your
);
2665 Your("amulet grows hot for a moment, then cools.");
2672 if (is_were(mdef
->data
) && mdef
->data
->mlet
!= S_HUMAN
)
2675 if (mdef
->data
== &mons
[PM_CLAY_GOLEM
]) {
2676 if (canseemon(mdef
))
2677 pline(writing_vanishes
, s_suffix(mon_nam(mdef
)));
2679 if (allow_cancel_kill
) {
2683 monkilled(mdef
, "", AD_SPEL
);
2690 /* you've zapped an immediate type wand up or down */
2693 struct obj
*obj
; /* wand or spell */
2695 boolean striking
= FALSE
, disclose
= FALSE
;
2696 int x
, y
, xx
, yy
, ptmp
;
2702 /* some wands have special effects other than normal bhitpile */
2703 /* drawbridge might change <u.ux,u.uy> */
2704 x
= xx
= u
.ux
; /* <x,y> is zap location */
2705 y
= yy
= u
.uy
; /* <xx,yy> is drawbridge (portcullis) position */
2706 ttmp
= t_at(x
, y
); /* trap if there is one */
2708 switch (obj
->otyp
) {
2712 You("probe towards the %s.", ceiling(x
, y
));
2714 ptmp
+= bhitpile(obj
, bhito
, x
, y
, u
.dz
);
2715 You("probe beneath the %s.", surface(x
, y
));
2716 ptmp
+= display_binventory(x
, y
, TRUE
);
2719 Your("probe reveals nothing.");
2720 return TRUE
; /* we've done our own bhitpile */
2723 /* up or down, but at closed portcullis only */
2724 if (is_db_wall(x
, y
) && find_drawbridge(&xx
, &yy
)) {
2725 open_drawbridge(xx
, yy
);
2727 } else if (u
.dz
> 0 && (x
== xdnstair
&& y
== ydnstair
)
2728 /* can't use the stairs down to quest level 2 until
2729 leader "unlocks" them; give feedback if you try */
2730 && on_level(&u
.uz
, &qstart_level
) && !ok_to_quest()) {
2731 pline_The("stairs seem to ripple momentarily.");
2734 /* down will release you from bear trap or web */
2735 if (u
.dz
> 0 && u
.utrap
) {
2736 (void) openholdingtrap(&youmonst
, &disclose
);
2737 /* down will trigger trapdoor, hole, or [spiked-] pit */
2738 } else if (u
.dz
> 0 && !u
.utrap
) {
2739 (void) openfallingtrap(&youmonst
, FALSE
, &disclose
);
2743 case SPE_FORCE_BOLT
:
2747 case SPE_WIZARD_LOCK
:
2748 /* down at open bridge or up or down at open portcullis */
2749 if (((levl
[x
][y
].typ
== DRAWBRIDGE_DOWN
)
2751 : (is_drawbridge_wall(x
, y
) >= 0 && !is_db_wall(x
, y
)))
2752 && find_drawbridge(&xx
, &yy
)) {
2754 close_drawbridge(xx
, yy
);
2756 destroy_drawbridge(xx
, yy
);
2758 } else if (striking
&& u
.dz
< 0 && rn2(3) && !Is_airlevel(&u
.uz
)
2759 && !Is_waterlevel(&u
.uz
) && !Underwater
2760 && !Is_qstart(&u
.uz
)) {
2762 /* similar to zap_dig() */
2763 pline("A rock is dislodged from the %s and falls on your %s.",
2764 ceiling(x
, y
), body_part(HEAD
));
2765 dmg
= rnd((uarmh
&& is_metallic(uarmh
)) ? 2 : 6);
2766 losehp(Maybe_Half_Phys(dmg
), "falling rock", KILLED_BY_AN
);
2767 if ((otmp
= mksobj_at(ROCK
, x
, y
, FALSE
, FALSE
)) != 0) {
2768 (void) xname(otmp
); /* set dknown, maybe bknown */
2772 } else if (u
.dz
> 0 && ttmp
) {
2773 if (!striking
&& closeholdingtrap(&youmonst
, &disclose
)) {
2774 ; /* now stuck in web or bear trap */
2775 } else if (striking
&& ttmp
->ttyp
== TRAPDOOR
) {
2776 /* striking transforms trapdoor into hole */
2777 if (Blind
&& !ttmp
->tseen
) {
2778 pline("%s beneath you shatters.", Something
);
2779 } else if (!ttmp
->tseen
) { /* => !Blind */
2780 pline("There's a trapdoor beneath you; it shatters.");
2782 pline("The trapdoor beneath you shatters.");
2788 /* might fall down hole */
2790 } else if (!striking
&& ttmp
->ttyp
== HOLE
) {
2791 /* locking transforms hole into trapdoor */
2792 ttmp
->ttyp
= TRAPDOOR
;
2793 if (Blind
|| !ttmp
->tseen
) {
2794 pline("Some %s swirls beneath you.",
2795 is_ice(x
, y
) ? "frost" : "dust");
2799 pline("A trapdoor appears beneath you.");
2802 /* hadn't fallen down hole; won't fall now */
2806 case SPE_STONE_TO_FLESH
:
2807 if (Is_airlevel(&u
.uz
) || Is_waterlevel(&u
.uz
) || Underwater
2808 || (Is_qstart(&u
.uz
) && u
.dz
< 0)) {
2809 pline1(nothing_happens
);
2810 } else if (u
.dz
< 0) { /* we should do more... */
2811 pline("Blood drips on your %s.", body_part(FACE
));
2812 } else if (u
.dz
> 0 && !OBJ_AT(u
.ux
, u
.uy
)) {
2814 Print this message only if there wasn't an engraving
2815 affected here. If water or ice, act like waterlevel case.
2817 e
= engr_at(u
.ux
, u
.uy
);
2818 if (!(e
&& e
->engr_type
== ENGRAVE
)) {
2819 if (is_pool(u
.ux
, u
.uy
) || is_ice(u
.ux
, u
.uy
))
2820 pline1(nothing_happens
);
2822 pline("Blood %ss %s your %s.",
2823 is_lava(u
.ux
, u
.uy
) ? "boil" : "pool",
2824 Levitation
? "beneath" : "at",
2825 makeplural(body_part(FOOT
)));
2834 /* zapping downward */
2835 (void) bhitpile(obj
, bhito
, x
, y
, u
.dz
);
2837 /* subset of engraving effects; none sets `disclose' */
2838 if ((e
= engr_at(x
, y
)) != 0 && e
->engr_type
!= HEADSTONE
) {
2839 switch (obj
->otyp
) {
2843 make_engr_at(x
, y
, random_engraving(buf
), moves
, (xchar
) 0);
2845 case WAN_CANCELLATION
:
2846 case SPE_CANCELLATION
:
2847 case WAN_MAKE_INVISIBLE
:
2850 case WAN_TELEPORTATION
:
2851 case SPE_TELEPORT_AWAY
:
2854 case SPE_STONE_TO_FLESH
:
2855 if (e
->engr_type
== ENGRAVE
) {
2856 /* only affects things in stone */
2857 pline_The(Hallucination
2858 ? "floor runs like butter!"
2859 : "edges on the floor get smoother.");
2860 wipe_engr_at(x
, y
, d(2, 4), TRUE
);
2864 case SPE_FORCE_BOLT
:
2865 wipe_engr_at(x
, y
, d(2, 4), TRUE
);
2871 } else if (u
.dz
< 0) {
2872 /* zapping upward */
2874 /* game flavor: if you're hiding under "something"
2875 * a zap upward should hit that "something".
2877 if (u
.uundetected
&& hides_under(youmonst
.data
)) {
2879 otmp
= level
.objects
[u
.ux
][u
.uy
];
2882 hitit
= bhito(otmp
, obj
);
2884 (void) hideunder(&youmonst
);
2893 /* used by do_break_wand() was well as by weffects() */
2903 /* if do_osshock() set obj_zapped while polying, give a message now */
2905 You_feel("shuddering vibrations.");
2909 /* called for various wand and spell effects - M. Stephenson */
2914 int otyp
= obj
->otyp
;
2915 boolean disclose
= FALSE
, was_unkn
= !objects
[otyp
].oc_name_known
;
2917 exercise(A_WIS
, TRUE
);
2918 if (u
.usteed
&& (objects
[otyp
].oc_dir
!= NODIR
) && !u
.dx
&& !u
.dy
2919 && (u
.dz
> 0) && zap_steed(obj
)) {
2921 } else if (objects
[otyp
].oc_dir
== IMMEDIATE
) {
2922 zapsetup(); /* reset obj_zapped */
2924 (void) bhitm(u
.ustuck
, obj
);
2925 /* [how about `bhitpile(u.ustuck->minvent)' effect?] */
2927 disclose
= zap_updown(obj
);
2929 (void) bhit(u
.dx
, u
.dy
, rn1(8, 6), ZAPPED_WAND
, bhitm
, bhito
,
2932 zapwrapup(); /* give feedback for obj_zapped */
2934 } else if (objects
[otyp
].oc_dir
== NODIR
) {
2938 /* neither immediate nor directionless */
2940 if (otyp
== WAN_DIGGING
|| otyp
== SPE_DIG
)
2942 else if (otyp
>= SPE_MAGIC_MISSILE
&& otyp
<= SPE_FINGER_OF_DEATH
)
2943 buzz(otyp
- SPE_MAGIC_MISSILE
+ 10, u
.ulevel
/ 2 + 1, u
.ux
, u
.uy
,
2945 else if (otyp
>= WAN_MAGIC_MISSILE
&& otyp
<= WAN_LIGHTNING
)
2946 buzz(otyp
- WAN_MAGIC_MISSILE
,
2947 (otyp
== WAN_MAGIC_MISSILE
) ? 2 : 6, u
.ux
, u
.uy
, u
.dx
, u
.dy
);
2949 impossible("weffects: unexpected spell or wand");
2955 more_experienced(0, 10);
2960 /* augment damage for a spell dased on the hero's intelligence (and level) */
2962 spell_damage_bonus(dmg
)
2963 int dmg
; /* base amount to be adjusted by bonus or penalty */
2965 int intell
= ACURR(A_INT
);
2967 /* Punish low intelligence before low level else low intelligence
2968 gets punished only when high level */
2970 /* -3 penalty, but never reduce combined amount below 1
2971 (if dmg is 0 for some reason, we're careful to leave it there) */
2973 dmg
= (dmg
<= 3) ? 1 : dmg
- 3;
2974 } else if (intell
<= 13 || u
.ulevel
< 5)
2975 ; /* no bonus or penalty; dmg remains same */
2976 else if (intell
<= 18)
2978 else if (intell
<= 24 || u
.ulevel
< 14)
2981 dmg
+= 3; /* Int 25 */
2987 * Generate the to hit bonus for a spell. Based on the hero's skill in
2988 * spell class and dexterity.
2991 spell_hit_bonus(skill
)
2995 int dex
= ACURR(A_DEX
);
2997 switch (P_SKILL(spell_skilltype(skill
))) {
2998 case P_ISRESTRICTED
:
3020 /* Will change when print stuff below removed */
3023 /* Even increment for dextrous heroes (see weapon.c abon) */
3024 hit_bon
+= dex
- 14;
3033 /* force == 0 occurs e.g. with sleep ray */
3034 /* note that large force is usual with wands so that !! would
3035 require information about hand/weapon/wand */
3036 return (const char *) ((force
< 0) ? "?" : (force
<= 4) ? "." : "!");
3040 hit(str
, mtmp
, force
)
3043 const char *force
; /* usually either "." or "!" */
3045 if ((!cansee(bhitpos
.x
, bhitpos
.y
) && !canspotmon(mtmp
)
3046 && !(u
.uswallow
&& mtmp
== u
.ustuck
)) || !flags
.verbose
)
3047 pline("%s %s it.", The(str
), vtense(str
, "hit"));
3049 pline("%s %s %s%s", The(str
), vtense(str
, "hit"),
3050 mon_nam(mtmp
), force
);
3055 register const char *str
;
3056 register struct monst
*mtmp
;
3059 "%s %s %s.", The(str
), vtense(str
, "miss"),
3060 ((cansee(bhitpos
.x
, bhitpos
.y
) || canspotmon(mtmp
)) && flags
.verbose
)
3066 skiprange(range
, skipstart
, skipend
)
3067 int range
, *skipstart
, *skipend
;
3069 int tr
= (range
/ 4);
3070 int tmp
= range
- ((tr
> 0) ? rnd(tr
) : 0);
3072 *skipend
= tmp
- ((tmp
/ 4) * rnd(3));
3073 if (*skipend
>= tmp
)
3078 * Called for the following distance effects:
3079 * when a weapon is thrown (weapon == THROWN_WEAPON)
3080 * when an object is kicked (KICKED_WEAPON)
3081 * when an IMMEDIATE wand is zapped (ZAPPED_WAND)
3082 * when a light beam is flashed (FLASHED_LIGHT)
3083 * when a mirror is applied (INVIS_BEAM)
3084 * A thrown/kicked object falls down at end of its range or when a monster
3085 * is hit. The variable 'bhitpos' is set to the final position of the weapon
3086 * thrown/zapped. The ray of a wand may affect (by calling a provided
3087 * function) several objects and monsters on its path. The return value
3088 * is the monster hit (weapon != ZAPPED_WAND), or a null monster pointer.
3090 * Thrown and kicked objects (THROWN_WEAPON or KICKED_WEAPON) may be
3091 * destroyed and *pobj set to NULL to indicate this.
3093 * Check !u.uswallow before calling bhit().
3094 * This function reveals the absence of a remembered invisible monster in
3095 * necessary cases (throwing or kicking weapons). The presence of a real
3096 * one is revealed for a weapon, but if not a weapon is left up to fhitm().
3099 bhit(ddx
, ddy
, range
, weapon
, fhitm
, fhito
, pobj
)
3100 register int ddx
, ddy
, range
; /* direction and range */
3101 int weapon
; /* see values in hack.h */
3102 int FDECL((*fhitm
), (MONST_P
, OBJ_P
)), /* fns called when mon/obj hit */
3103 FDECL((*fhito
), (OBJ_P
, OBJ_P
));
3104 struct obj
**pobj
; /* object tossed/used, set to NULL
3105 * if object is destroyed */
3108 struct obj
*obj
= *pobj
;
3110 boolean shopdoor
= FALSE
, point_blank
= TRUE
;
3111 boolean in_skip
= FALSE
, allow_skip
= FALSE
;
3112 int skiprange_start
= 0, skiprange_end
= 0, skipcount
= 0;
3114 if (weapon
== KICKED_WEAPON
) {
3115 /* object starts one square in front of player */
3116 bhitpos
.x
= u
.ux
+ ddx
;
3117 bhitpos
.y
= u
.uy
+ ddy
;
3124 if (weapon
== THROWN_WEAPON
&& obj
&& obj
->otyp
== ROCK
) {
3125 skiprange(range
, &skiprange_start
, &skiprange_end
);
3126 allow_skip
= !rn2(3);
3129 if (weapon
== FLASHED_LIGHT
) {
3130 tmp_at(DISP_BEAM
, cmap_to_glyph(S_flashbeam
));
3131 } else if (weapon
!= ZAPPED_WAND
&& weapon
!= INVIS_BEAM
)
3132 tmp_at(DISP_FLASH
, obj_to_glyph(obj
));
3134 while (range
-- > 0) {
3148 if (is_pick(obj
) && inside_shop(x
, y
)
3149 && (mtmp
= shkcatch(obj
, x
, y
)) != 0) {
3150 tmp_at(DISP_END
, 0);
3154 typ
= levl
[bhitpos
.x
][bhitpos
.y
].typ
;
3156 /* iron bars will block anything big enough */
3157 if ((weapon
== THROWN_WEAPON
|| weapon
== KICKED_WEAPON
)
3159 && hits_bars(pobj
, x
- ddx
, y
- ddy
, bhitpos
.x
, bhitpos
.y
,
3160 point_blank
? 0 : !rn2(5), 1)) {
3161 /* caveat: obj might now be null... */
3168 if (weapon
== ZAPPED_WAND
&& find_drawbridge(&x
, &y
)) {
3169 boolean learn_it
= FALSE
;
3171 switch (obj
->otyp
) {
3174 if (is_db_wall(bhitpos
.x
, bhitpos
.y
)) {
3175 if (cansee(x
, y
) || cansee(bhitpos
.x
, bhitpos
.y
))
3177 open_drawbridge(x
, y
);
3181 case SPE_WIZARD_LOCK
:
3182 if ((cansee(x
, y
) || cansee(bhitpos
.x
, bhitpos
.y
))
3183 && levl
[x
][y
].typ
== DRAWBRIDGE_DOWN
)
3185 close_drawbridge(x
, y
);
3188 case SPE_FORCE_BOLT
:
3189 if (typ
!= DRAWBRIDGE_UP
)
3190 destroy_drawbridge(x
, y
);
3198 mtmp
= m_at(bhitpos
.x
, bhitpos
.y
);
3203 * skiprange_start is only set if this is a thrown rock
3205 if (skiprange_start
&& (range
== skiprange_start
) && allow_skip
) {
3206 if (is_pool(bhitpos
.x
, bhitpos
.y
) && !mtmp
) {
3209 pline("%s %s%s.", Yname2(obj
), otense(obj
, "skip"),
3210 skipcount
? " again" : "");
3212 You_hear("%s skip.", yname(obj
));
3214 } else if (skiprange_start
> skiprange_end
+ 1) {
3219 if (range
<= skiprange_end
) {
3221 if (range
> 3) /* another bounce? */
3222 skiprange(range
, &skiprange_start
, &skiprange_end
);
3223 } else if (mtmp
&& M_IN_WATER(mtmp
->data
)) {
3224 if ((!Blind
&& canseemon(mtmp
)) || sensemon(mtmp
))
3225 pline("%s %s over %s.", Yname2(obj
), otense(obj
, "pass"),
3230 if (mtmp
&& !(in_skip
&& M_IN_WATER(mtmp
->data
))) {
3231 notonhead
= (bhitpos
.x
!= mtmp
->mx
|| bhitpos
.y
!= mtmp
->my
);
3232 if (weapon
== FLASHED_LIGHT
) {
3233 /* FLASHED_LIGHT hitting invisible monster should
3234 pass through instead of stop so we call
3235 flash_hits_mon() directly rather than returning
3236 mtmp back to caller. That allows the flash to
3237 keep on going. Note that we use mtmp->minvis
3238 not canspotmon() because it makes no difference
3239 whether the hero can see the monster or not. */
3241 obj
->ox
= u
.ux
, obj
->oy
= u
.uy
;
3242 (void) flash_hits_mon(mtmp
, obj
);
3244 tmp_at(DISP_END
, 0);
3245 return mtmp
; /* caller will call flash_hits_mon */
3247 } else if (weapon
== INVIS_BEAM
) {
3248 /* Like FLASHED_LIGHT, INVIS_BEAM should continue
3249 through invisible targets; unlike it, we aren't
3250 prepared for multiple hits so just get first one
3251 that's either visible or could see its invisible
3252 self. [No tmp_at() cleanup is needed here.] */
3253 if (!mtmp
->minvis
|| perceives(mtmp
->data
))
3255 } else if (weapon
!= ZAPPED_WAND
) {
3256 /* THROWN_WEAPON, KICKED_WEAPON */
3257 tmp_at(DISP_END
, 0);
3258 if (cansee(bhitpos
.x
, bhitpos
.y
) && !canspotmon(mtmp
))
3259 map_invisible(bhitpos
.x
, bhitpos
.y
);
3263 (*fhitm
)(mtmp
, obj
);
3267 if (weapon
== ZAPPED_WAND
&& obj
->otyp
== WAN_PROBING
3268 && glyph_is_invisible(levl
[bhitpos
.x
][bhitpos
.y
].glyph
)) {
3269 unmap_object(bhitpos
.x
, bhitpos
.y
);
3274 if (bhitpile(obj
, fhito
, bhitpos
.x
, bhitpos
.y
, 0))
3277 if (weapon
== KICKED_WEAPON
3278 && ((obj
->oclass
== COIN_CLASS
3279 && OBJ_AT(bhitpos
.x
, bhitpos
.y
))
3280 || ship_object(obj
, bhitpos
.x
, bhitpos
.y
,
3281 costly_spot(bhitpos
.x
, bhitpos
.y
)))) {
3282 tmp_at(DISP_END
, 0);
3283 return (struct monst
*) 0;
3286 if (weapon
== ZAPPED_WAND
&& (IS_DOOR(typ
) || typ
== SDOOR
)) {
3287 switch (obj
->otyp
) {
3292 case SPE_WIZARD_LOCK
:
3293 case SPE_FORCE_BOLT
:
3294 if (doorlock(obj
, bhitpos
.x
, bhitpos
.y
)) {
3295 if (cansee(bhitpos
.x
, bhitpos
.y
)
3296 || (obj
->otyp
== WAN_STRIKING
&& !Deaf
))
3298 if (levl
[bhitpos
.x
][bhitpos
.y
].doormask
== D_BROKEN
3299 && *in_rooms(bhitpos
.x
, bhitpos
.y
, SHOPBASE
)) {
3301 add_damage(bhitpos
.x
, bhitpos
.y
, 400L);
3307 if (!ZAP_POS(typ
) || closed_door(bhitpos
.x
, bhitpos
.y
)) {
3312 if (weapon
!= ZAPPED_WAND
&& weapon
!= INVIS_BEAM
) {
3313 /* 'I' present but no monster: erase */
3314 /* do this before the tmp_at() */
3315 if (glyph_is_invisible(levl
[bhitpos
.x
][bhitpos
.y
].glyph
)
3317 unmap_object(bhitpos
.x
, bhitpos
.y
);
3320 tmp_at(bhitpos
.x
, bhitpos
.y
);
3322 /* kicked objects fall in pools */
3323 if ((weapon
== KICKED_WEAPON
)
3324 && (is_pool(bhitpos
.x
, bhitpos
.y
)
3325 || is_lava(bhitpos
.x
, bhitpos
.y
)))
3327 if (IS_SINK(typ
) && weapon
!= FLASHED_LIGHT
)
3328 break; /* physical objects fall onto sink */
3330 /* limit range of ball so hero won't make an invalid move */
3331 if (weapon
== THROWN_WEAPON
&& range
> 0
3332 && obj
->otyp
== HEAVY_IRON_BALL
) {
3336 if ((bobj
= sobj_at(BOULDER
, x
, y
)) != 0) {
3338 pline("%s hits %s.", The(distant_name(obj
, xname
)),
3341 } else if (obj
== uball
) {
3342 if (!test_move(x
- ddx
, y
- ddy
, ddx
, ddy
, TEST_MOVE
)) {
3343 /* nb: it didn't hit anything directly */
3345 pline("%s jerks to an abrupt halt.",
3346 The(distant_name(obj
, xname
))); /* lame */
3348 } else if (Sokoban
&& (t
= t_at(x
, y
)) != 0
3349 && (t
->ttyp
== PIT
|| t
->ttyp
== SPIKED_PIT
3350 || t
->ttyp
== HOLE
|| t
->ttyp
== TRAPDOOR
)) {
3351 /* hero falls into the trap, so ball stops */
3357 /* thrown/kicked missile has moved away from its starting spot */
3358 point_blank
= FALSE
; /* affects passing through iron bars */
3361 if (weapon
!= ZAPPED_WAND
&& weapon
!= INVIS_BEAM
)
3362 tmp_at(DISP_END
, 0);
3365 pay_for_damage("destroy", FALSE
);
3367 return (struct monst
*) 0;
3370 /* process thrown boomerang, which travels a curving path...
3371 * A multi-shot volley ought to have all missiles in flight at once,
3372 * but we're called separately for each one. We terminate the volley
3373 * early on a failed catch since continuing to throw after being hit
3374 * is too obviously silly.
3377 boomhit(obj
, dx
, dy
)
3382 int boom
; /* showsym[] index */
3384 boolean counterclockwise
= TRUE
; /* right-handed throw */
3386 /* counterclockwise traversal patterns:
3387 * ..........................54.................................
3388 * ..................43.....6..3....765.........................
3389 * ..........32.....5..2...7...2...8...4....87..................
3390 * .........4..1....6..1...8..1....9...3...9..6.....98..........
3391 * ..21@....5...@...7..@....9@......@12....@...5...@..7.....@9..
3392 * .3...9....6..9....89.....................1..4...1..6....1..8.
3393 * .4...8.....78.............................23....2..5...2...7.
3394 * ..567............................................34....3..6..
3395 * ........................................................45...
3396 * (invert rows for corresponding clockwise patterns)
3401 boom
= counterclockwise
? S_boomleft
: S_boomright
;
3402 for (i
= 0; i
< 8; i
++)
3403 if (xdir
[i
] == dx
&& ydir
[i
] == dy
)
3405 tmp_at(DISP_FLASH
, cmap_to_glyph(boom
));
3406 for (ct
= 0; ct
< 10; ct
++) {
3407 i
= (i
+ 8) % 8; /* 0..7 (8 -> 0, -1 -> 7) */
3408 boom
= (S_boomleft
+ S_boomright
- boom
); /* toggle */
3409 tmp_at(DISP_CHANGE
, cmap_to_glyph(boom
)); /* change glyph */
3414 if ((mtmp
= m_at(bhitpos
.x
, bhitpos
.y
)) != 0) {
3416 tmp_at(DISP_END
, 0);
3419 if (!ZAP_POS(levl
[bhitpos
.x
][bhitpos
.y
].typ
)
3420 || closed_door(bhitpos
.x
, bhitpos
.y
)) {
3425 if (bhitpos
.x
== u
.ux
&& bhitpos
.y
== u
.uy
) { /* ct == 9 */
3426 if (Fumbling
|| rn2(20) >= ACURR(A_DEX
)) {
3427 /* we hit ourselves */
3428 (void) thitu(10 + obj
->spe
, dmgval(obj
, &youmonst
), obj
,
3432 } else { /* we catch it */
3433 tmp_at(DISP_END
, 0);
3434 You("skillfully catch the boomerang.");
3438 tmp_at(bhitpos
.x
, bhitpos
.y
);
3440 if (IS_SINK(levl
[bhitpos
.x
][bhitpos
.y
].typ
)) {
3443 break; /* boomerang falls on sink */
3445 /* ct==0, initial position, we want next delta to be same;
3446 ct==5, opposite position, repeat delta undoes first one */
3448 i
+= (counterclockwise
? -1 : 1);
3450 tmp_at(DISP_END
, 0); /* do not leave last symbol */
3451 return (struct monst
*) 0;
3454 /* used by buzz(); also used by munslime(muse.c); returns damage applied
3455 to mon; note: caller is responsible for killing mon if damage is fatal */
3457 zhitm(mon
, type
, nd
, ootmp
)
3458 register struct monst
*mon
;
3459 register int type
, nd
;
3460 struct obj
**ootmp
; /* to return worn armor for caller to disintegrate */
3462 register int tmp
= 0;
3463 register int abstype
= abs(type
) % 10;
3464 boolean sho_shieldeff
= FALSE
;
3465 boolean spellcaster
= is_hero_spell(type
); /* maybe get a bonus! */
3467 *ootmp
= (struct obj
*) 0;
3469 case ZT_MAGIC_MISSILE
:
3470 if (resists_magm(mon
)) {
3471 sho_shieldeff
= TRUE
;
3476 tmp
= spell_damage_bonus(tmp
);
3479 if (resists_fire(mon
)) {
3480 sho_shieldeff
= TRUE
;
3484 if (resists_cold(mon
))
3487 tmp
= spell_damage_bonus(tmp
);
3488 if (burnarmor(mon
)) {
3490 (void) destroy_mitem(mon
, POTION_CLASS
, AD_FIRE
);
3492 (void) destroy_mitem(mon
, SCROLL_CLASS
, AD_FIRE
);
3494 (void) destroy_mitem(mon
, SPBOOK_CLASS
, AD_FIRE
);
3495 destroy_mitem(mon
, FOOD_CLASS
, AD_FIRE
); /* carried slime */
3499 if (resists_cold(mon
)) {
3500 sho_shieldeff
= TRUE
;
3504 if (resists_fire(mon
))
3507 tmp
= spell_damage_bonus(tmp
);
3509 (void) destroy_mitem(mon
, POTION_CLASS
, AD_COLD
);
3513 (void) sleep_monst(mon
, d(nd
, 25),
3514 type
== ZT_WAND(ZT_SLEEP
) ? WAND_CLASS
: '\0');
3516 case ZT_DEATH
: /* death/disintegration */
3517 if (abs(type
) != ZT_BREATH(ZT_DEATH
)) { /* death */
3518 if (mon
->data
== &mons
[PM_DEATH
]) {
3519 mon
->mhpmax
+= mon
->mhpmax
/ 2;
3520 if (mon
->mhpmax
>= MAGIC_COOKIE
)
3521 mon
->mhpmax
= MAGIC_COOKIE
- 1;
3522 mon
->mhp
= mon
->mhpmax
;
3526 if (nonliving(mon
->data
) || is_demon(mon
->data
)
3527 || is_vampshifter(mon
) || resists_magm(mon
)) {
3528 /* similar to player */
3529 sho_shieldeff
= TRUE
;
3532 type
= -1; /* so they don't get saving throws */
3536 if (resists_disint(mon
)) {
3537 sho_shieldeff
= TRUE
;
3538 } else if (mon
->misc_worn_check
& W_ARMS
) {
3539 /* destroy shield; victim survives */
3540 *ootmp
= which_armor(mon
, W_ARMS
);
3541 } else if (mon
->misc_worn_check
& W_ARM
) {
3542 /* destroy body armor, also cloak if present */
3543 *ootmp
= which_armor(mon
, W_ARM
);
3544 if ((otmp2
= which_armor(mon
, W_ARMC
)) != 0)
3545 m_useup(mon
, otmp2
);
3547 /* no body armor, victim dies; destroy cloak
3548 and shirt now in case target gets life-saved */
3550 if ((otmp2
= which_armor(mon
, W_ARMC
)) != 0)
3551 m_useup(mon
, otmp2
);
3552 if ((otmp2
= which_armor(mon
, W_ARMU
)) != 0)
3553 m_useup(mon
, otmp2
);
3555 type
= -1; /* no saving throw wanted */
3556 break; /* not ordinary damage */
3561 if (resists_elec(mon
)) {
3562 sho_shieldeff
= TRUE
;
3564 /* can still blind the monster */
3568 tmp
= spell_damage_bonus(tmp
);
3569 if (!resists_blnd(mon
)
3570 && !(type
> 0 && u
.uswallow
&& mon
== u
.ustuck
)) {
3571 register unsigned rnd_tmp
= rnd(50);
3573 if ((mon
->mblinded
+ rnd_tmp
) > 127)
3574 mon
->mblinded
= 127;
3576 mon
->mblinded
+= rnd_tmp
;
3579 (void) destroy_mitem(mon
, WAND_CLASS
, AD_ELEC
);
3580 /* not actually possible yet */
3582 (void) destroy_mitem(mon
, RING_CLASS
, AD_ELEC
);
3585 if (resists_poison(mon
)) {
3586 sho_shieldeff
= TRUE
;
3592 if (resists_acid(mon
)) {
3593 sho_shieldeff
= TRUE
;
3598 acid_damage(MON_WEP(mon
));
3600 erode_armor(mon
, ERODE_CORRODE
);
3604 shieldeff(mon
->mx
, mon
->my
);
3605 if (is_hero_spell(type
) && (Role_if(PM_KNIGHT
) && u
.uhave
.questart
))
3607 if (tmp
> 0 && type
>= 0
3608 && resist(mon
, type
< ZT_SPELL(0) ? WAND_CLASS
: '\0', 0, NOTELL
))
3611 tmp
= 0; /* don't allow negative damage */
3612 debugpline3("zapped monster hp = %d (= %d - %d)", mon
->mhp
- tmp
,
3619 zhitu(type
, nd
, fltxt
, sx
, sy
)
3624 int dam
= 0, abstyp
= abs(type
);
3626 switch (abstyp
% 10) {
3627 case ZT_MAGIC_MISSILE
:
3630 pline_The("missiles bounce off!");
3633 exercise(A_STR
, FALSE
);
3637 if (Fire_resistance
) {
3639 You("don't feel hot!");
3640 ugolemeffects(AD_FIRE
, d(nd
, 6));
3645 if (burnarmor(&youmonst
)) { /* "body hit" */
3647 destroy_item(POTION_CLASS
, AD_FIRE
);
3649 destroy_item(SCROLL_CLASS
, AD_FIRE
);
3651 destroy_item(SPBOOK_CLASS
, AD_FIRE
);
3652 destroy_item(FOOD_CLASS
, AD_FIRE
);
3656 if (Cold_resistance
) {
3658 You("don't feel cold.");
3659 ugolemeffects(AD_COLD
, d(nd
, 6));
3664 destroy_item(POTION_CLASS
, AD_COLD
);
3667 if (Sleep_resistance
) {
3668 shieldeff(u
.ux
, u
.uy
);
3669 You("don't feel sleepy.");
3671 fall_asleep(-d(nd
, 25), TRUE
); /* sleep ray */
3675 if (abstyp
== ZT_BREATH(ZT_DEATH
)) {
3676 if (Disint_resistance
) {
3677 You("are not disintegrated.");
3680 /* destroy shield; other possessions are safe */
3681 (void) destroy_arm(uarms
);
3684 /* destroy suit; if present, cloak goes too */
3686 (void) destroy_arm(uarmc
);
3687 (void) destroy_arm(uarm
);
3690 /* no shield or suit, you're dead; wipe out cloak
3691 and/or shirt in case of life-saving or bones */
3693 (void) destroy_arm(uarmc
);
3695 (void) destroy_arm(uarmu
);
3696 } else if (nonliving(youmonst
.data
) || is_demon(youmonst
.data
)) {
3698 You("seem unaffected.");
3700 } else if (Antimagic
) {
3702 You("aren't affected.");
3705 killer
.format
= KILLED_BY_AN
;
3706 Strcpy(killer
.name
, fltxt
? fltxt
: "");
3707 /* when killed by disintegration breath, don't leave corpse */
3708 u
.ugrave_arise
= (type
== -ZT_BREATH(ZT_DEATH
)) ? -3 : NON_PM
;
3710 return; /* lifesaved */
3712 if (Shock_resistance
) {
3714 You("aren't affected.");
3715 ugolemeffects(AD_ELEC
, d(nd
, 6));
3718 exercise(A_CON
, FALSE
);
3721 destroy_item(WAND_CLASS
, AD_ELEC
);
3723 destroy_item(RING_CLASS
, AD_ELEC
);
3726 poisoned("blast", A_DEX
, "poisoned blast", 15, FALSE
);
3729 if (Acid_resistance
) {
3730 pline_The("%s doesn't hurt.", hliquid("acid"));
3733 pline_The("%s burns!", hliquid("acid"));
3735 exercise(A_STR
, FALSE
);
3737 /* using two weapons at once makes both of them more vulnerable */
3738 if (!rn2(u
.twoweap
? 3 : 6))
3740 if (u
.twoweap
&& !rn2(3))
3741 acid_damage(uswapwep
);
3743 erode_armor(&youmonst
, ERODE_CORRODE
);
3747 /* Half_spell_damage protection yields half-damage for wands & spells,
3748 including hero's own ricochets; breath attacks do full damage */
3749 if (dam
&& Half_spell_damage
&& !(abstyp
>= 20 && abstyp
<= 29))
3750 dam
= (dam
+ 1) / 2;
3751 losehp(dam
, fltxt
, KILLED_BY_AN
);
3756 * burn objects (such as scrolls and spellbooks) on floor
3757 * at position x,y; return the number of objects burned
3760 burn_floor_objects(x
, y
, give_feedback
, u_caused
)
3762 boolean give_feedback
; /* caller needs to decide about visibility checks */
3765 struct obj
*obj
, *obj2
;
3766 long i
, scrquan
, delquan
;
3767 char buf1
[BUFSZ
], buf2
[BUFSZ
];
3770 for (obj
= level
.objects
[x
][y
]; obj
; obj
= obj2
) {
3771 obj2
= obj
->nexthere
;
3772 if (obj
->oclass
== SCROLL_CLASS
|| obj
->oclass
== SPBOOK_CLASS
3773 || (obj
->oclass
== FOOD_CLASS
3774 && obj
->otyp
== GLOB_OF_GREEN_SLIME
)) {
3775 if (obj
->otyp
== SCR_FIRE
|| obj
->otyp
== SPE_FIREBALL
3776 || obj_resists(obj
, 2, 100))
3778 scrquan
= obj
->quan
; /* number present */
3779 delquan
= 0L; /* number to destroy */
3780 for (i
= scrquan
; i
> 0L; i
--)
3784 /* save name before potential delobj() */
3785 if (give_feedback
) {
3787 Strcpy(buf1
, (x
== u
.ux
&& y
== u
.uy
)
3789 : distant_name(obj
, xname
));
3791 Strcpy(buf2
, (x
== u
.ux
&& y
== u
.uy
)
3793 : distant_name(obj
, xname
));
3794 obj
->quan
= scrquan
;
3796 /* useupf(), which charges, only if hero caused damage */
3798 useupf(obj
, delquan
);
3799 else if (delquan
< scrquan
)
3800 obj
->quan
-= delquan
;
3804 if (give_feedback
) {
3806 pline("%ld %s burn.", delquan
, buf2
);
3808 pline("%s burns.", An(buf1
));
3816 /* will zap/spell/breath attack score a hit against armor class `ac'? */
3820 int type
; /* either hero cast spell type or 0 */
3822 int chance
= rn2(20);
3823 int spell_bonus
= type
? spell_hit_bonus(type
) : 0;
3825 /* small chance for naked target to avoid being hit */
3827 return rnd(10) < ac
+ spell_bonus
;
3829 /* very high armor protection does not achieve invulnerability */
3832 return (3 - chance
< ac
+ spell_bonus
);
3836 disintegrate_mon(mon
, type
, fltxt
)
3838 int type
; /* hero vs other */
3841 struct obj
*otmp
, *otmp2
, *m_amulet
= mlifesaver(mon
);
3843 if (canseemon(mon
)) {
3845 pline("%s is disintegrated!", Monnam(mon
));
3847 hit(fltxt
, mon
, "!");
3850 /* note: worn amulet of life saving must be preserved in order to operate */
3851 #define oresist_disintegration(obj) \
3852 (objects[obj->otyp].oc_oprop == DISINT_RES || obj_resists(obj, 5, 50) \
3853 || is_quest_artifact(obj) || obj == m_amulet)
3855 for (otmp
= mon
->minvent
; otmp
; otmp
= otmp2
) {
3857 if (!oresist_disintegration(otmp
)) {
3858 if (otmp
->owornmask
) {
3859 /* in case monster's life gets saved */
3860 mon
->misc_worn_check
&= ~otmp
->owornmask
;
3861 if (otmp
->owornmask
& W_WEP
)
3862 setmnotwielded(mon
, otmp
);
3863 /* also dismounts hero if this object is steed's saddle */
3864 update_mon_intrinsics(mon
, otmp
, FALSE
, TRUE
);
3865 otmp
->owornmask
= 0L;
3867 obj_extract_self(otmp
);
3868 obfree(otmp
, (struct obj
*) 0);
3872 #undef oresist_disintegration
3875 monkilled(mon
, (char *) 0, -AD_RBRE
);
3877 xkilled(mon
, XKILL_NOMSG
| XKILL_NOCORPSE
);
3881 buzz(type
,nd
,sx
,sy
,dx
,dy
)
3886 dobuzz(type
, nd
, sx
, sy
, dx
, dy
, TRUE
);
3890 * type == 0 to 9 : you shooting a wand
3891 * type == 10 to 19 : you casting a spell
3892 * type == 20 to 29 : you breathing as a monster
3893 * type == -10 to -19 : monster casting spell
3894 * type == -20 to -29 : monster breathing at you
3895 * type == -30 to -39 : monster shooting a wand
3896 * called with dx = dy = 0 with vertical bolts
3899 dobuzz(type
, nd
, sx
, sy
, dx
, dy
,say
)
3900 register int type
, nd
;
3901 register xchar sx
, sy
;
3902 register int dx
, dy
;
3903 boolean say
; /* Announce out of sight hit/miss events if true */
3905 int range
, abstype
= abs(type
) % 10;
3906 register xchar lsx
, lsy
;
3909 boolean shopdamage
= FALSE
;
3914 /* if its a Hero Spell then get its SPE_TYPE */
3915 spell_type
= is_hero_spell(type
) ? SPE_MAGIC_MISSILE
+ abstype
: 0;
3917 fltxt
= flash_types
[(type
<= -30) ? abstype
: abs(type
)];
3923 tmp
= zhitm(u
.ustuck
, type
, nd
, &otmp
);
3927 pline("%s rips into %s%s", The(fltxt
), mon_nam(u
.ustuck
),
3929 /* Using disintegration from the inside only makes a hole... */
3930 if (tmp
== MAGIC_COOKIE
)
3932 if (u
.ustuck
->mhp
< 1)
3939 if (dx
== 0 && dy
== 0)
3941 save_bhitpos
= bhitpos
;
3943 tmp_at(DISP_BEAM
, zapdir_to_glyph(dx
, dy
, abstype
));
3944 while (range
-- > 0) {
3949 if (!isok(sx
, sy
) || levl
[sx
][sy
].typ
== STONE
)
3953 if (cansee(sx
, sy
)) {
3954 /* reveal/unreveal invisible monsters before tmp_at() */
3955 if (mon
&& !canspotmon(mon
))
3956 map_invisible(sx
, sy
);
3957 else if (!mon
&& glyph_is_invisible(levl
[sx
][sy
].glyph
)) {
3958 unmap_object(sx
, sy
);
3961 if (ZAP_POS(levl
[sx
][sy
].typ
)
3962 || (isok(lsx
, lsy
) && cansee(lsx
, lsy
)))
3964 delay_output(); /* wait a little */
3967 /* hit() and miss() need bhitpos to match the target */
3968 bhitpos
.x
= sx
, bhitpos
.y
= sy
;
3969 /* Fireballs only damage when they explode */
3970 if (type
!= ZT_SPELL(ZT_FIRE
))
3971 range
+= zap_over_floor(sx
, sy
, type
, &shopdamage
, 0);
3974 if (type
== ZT_SPELL(ZT_FIRE
))
3977 mon
->mstrategy
&= ~STRAT_WAITMASK
;
3979 notonhead
= (mon
->mx
!= bhitpos
.x
|| mon
->my
!= bhitpos
.y
);
3980 if (zap_hit(find_mac(mon
), spell_type
)) {
3981 if (mon_reflects(mon
, (char *) 0)) {
3982 if (cansee(mon
->mx
, mon
->my
)) {
3983 hit(fltxt
, mon
, exclam(0));
3984 shieldeff(mon
->mx
, mon
->my
);
3985 (void) mon_reflects(mon
,
3986 "But it reflects from %s %s!");
3991 boolean mon_could_move
= mon
->mcanmove
;
3992 int tmp
= zhitm(mon
, type
, nd
, &otmp
);
3994 if (is_rider(mon
->data
)
3995 && abs(type
) == ZT_BREATH(ZT_DEATH
)) {
3996 if (canseemon(mon
)) {
3997 hit(fltxt
, mon
, ".");
3998 pline("%s disintegrates.", Monnam(mon
));
3999 pline("%s body reintegrates before your %s!",
4000 s_suffix(Monnam(mon
)),
4001 (eyecount(youmonst
.data
) == 1)
4003 : makeplural(body_part(EYE
)));
4004 pline("%s resurrects!", Monnam(mon
));
4006 mon
->mhp
= mon
->mhpmax
;
4007 break; /* Out of while loop */
4009 if (mon
->data
== &mons
[PM_DEATH
] && abstype
== ZT_DEATH
) {
4010 if (canseemon(mon
)) {
4011 hit(fltxt
, mon
, ".");
4012 pline("%s absorbs the deadly %s!", Monnam(mon
),
4013 type
== ZT_BREATH(ZT_DEATH
) ? "blast"
4015 pline("It seems even stronger than before.");
4017 break; /* Out of while loop */
4020 if (tmp
== MAGIC_COOKIE
) { /* disintegration */
4021 disintegrate_mon(mon
, type
, fltxt
);
4022 } else if (mon
->mhp
< 1) {
4024 monkilled(mon
, fltxt
, AD_RBRE
);
4029 /* normal non-fatal hit */
4030 if (say
|| canseemon(mon
))
4031 hit(fltxt
, mon
, exclam(tmp
));
4033 /* some armor was destroyed; no damage done */
4035 pline("%s %s is disintegrated!",
4036 s_suffix(Monnam(mon
)),
4037 distant_name(otmp
, xname
));
4040 if (mon_could_move
&& !mon
->mcanmove
) /* ZT_SLEEP */
4046 if (say
|| canseemon(mon
))
4049 } else if (sx
== u
.ux
&& sy
== u
.uy
&& range
>= 0) {
4051 if (u
.usteed
&& !rn2(3) && !mon_reflects(u
.usteed
, (char *) 0)) {
4054 } else if (zap_hit((int) u
.uac
, 0)) {
4056 pline("%s hits you!", The(fltxt
));
4059 (void) ureflects("But %s reflects from your %s!",
4062 pline("For some reason you are not affected.");
4067 zhitu(type
, nd
, fltxt
, sx
, sy
);
4069 } else if (!Blind
) {
4070 pline("%s whizzes by you!", The(fltxt
));
4071 } else if (abstype
== ZT_LIGHTNING
) {
4072 Your("%s tingles.", body_part(ARM
));
4074 if (abstype
== ZT_LIGHTNING
)
4075 (void) flashburn((long) d(nd
, 50));
4080 if (!ZAP_POS(levl
[sx
][sy
].typ
)
4081 || (closed_door(sx
, sy
) && range
>= 0)) {
4082 int bounce
, bchance
;
4087 bchance
= (levl
[sx
][sy
].typ
== STONE
) ? 10
4088 : (In_mines(&u
.uz
) && IS_WALL(levl
[sx
][sy
].typ
)) ? 20
4091 fireball
= (type
== ZT_SPELL(ZT_FIRE
));
4092 if ((--range
> 0 && isok(lsx
, lsy
) && cansee(lsx
, lsy
))
4094 if (Is_airlevel(&u
.uz
)) { /* nothing to bounce off of */
4095 pline_The("%s vanishes into the aether!", fltxt
);
4097 type
= ZT_WAND(ZT_FIRE
); /* skip pending fireball */
4099 } else if (fireball
) {
4102 break; /* fireballs explode before the obstacle */
4104 pline_The("%s bounces!", fltxt
);
4106 if (!dx
|| !dy
|| !rn2(bchance
)) {
4110 if (isok(sx
, lsy
) && ZAP_POS(rmn
= levl
[sx
][lsy
].typ
)
4111 && !closed_door(sx
, lsy
)
4112 && (IS_ROOM(rmn
) || (isok(sx
+ dx
, lsy
)
4113 && ZAP_POS(levl
[sx
+ dx
][lsy
].typ
))))
4115 if (isok(lsx
, sy
) && ZAP_POS(rmn
= levl
[lsx
][sy
].typ
)
4116 && !closed_door(lsx
, sy
)
4117 && (IS_ROOM(rmn
) || (isok(lsx
, sy
+ dy
)
4118 && ZAP_POS(levl
[lsx
][sy
+ dy
].typ
))))
4119 if (!bounce
|| rn2(2))
4124 dx
= -dx
; /* fall into... */
4132 tmp_at(DISP_CHANGE
, zapdir_to_glyph(dx
, dy
, abstype
));
4136 tmp_at(DISP_END
, 0);
4137 if (type
== ZT_SPELL(ZT_FIRE
))
4138 explode(sx
, sy
, type
, d(12, 6), 0, EXPL_FIERY
);
4140 pay_for_damage(abstype
== ZT_FIRE
4142 : abstype
== ZT_COLD
4144 /* "damage" indicates wall rather than door */
4145 : abstype
== ZT_ACID
4147 : abstype
== ZT_DEATH
4151 bhitpos
= save_bhitpos
;
4159 struct rm
*lev
= &levl
[x
][y
];
4163 msg
= "The ice crackles and melts.";
4164 if (lev
->typ
== DRAWBRIDGE_UP
) {
4165 lev
->drawbridgemask
&= ~DB_ICE
; /* revert to DB_MOAT */
4166 } else { /* lev->typ == ICE */
4168 if (lev
->icedpool
== ICED_POOL
)
4173 lev
->typ
= (lev
->icedpool
== ICED_POOL
? POOL
: MOAT
);
4177 spot_stop_timers(x
, y
, MELT_ICE_AWAY
); /* no more ice to melt away */
4178 obj_ice_effects(x
, y
, FALSE
);
4185 if ((otmp
= sobj_at(BOULDER
, x
, y
)) != 0) {
4187 pline("%s settles...", An(xname(otmp
)));
4189 obj_extract_self(otmp
); /* boulder isn't being pushed */
4190 if (!boulder_hits_pool(otmp
, x
, y
, FALSE
))
4191 impossible("melt_ice: no pool?");
4192 /* try again if there's another boulder and pool didn't fill */
4193 } while (is_pool(x
, y
) && (otmp
= sobj_at(BOULDER
, x
, y
)) != 0);
4196 if (x
== u
.ux
&& y
== u
.uy
)
4197 spoteffects(TRUE
); /* possibly drown, notice objects */
4200 #define MIN_ICE_TIME 50
4201 #define MAX_ICE_TIME 2000
4203 * Usually start a melt_ice timer; sometimes the ice will become
4204 * permanent instead.
4207 start_melt_ice_timeout(x
, y
, min_time
)
4209 long min_time
; /* <x,y>'s old melt timeout (deleted by time we get here) */
4214 when
= (int) min_time
;
4215 if (when
< MIN_ICE_TIME
- 1)
4216 when
= MIN_ICE_TIME
- 1;
4218 /* random timeout; surrounding ice locations ought to be a factor... */
4219 while (++when
<= MAX_ICE_TIME
)
4220 if (!rn2((MAX_ICE_TIME
- when
) + MIN_ICE_TIME
))
4223 /* if we're within MAX_ICE_TIME, install a melt timer;
4224 otherwise, omit it to leave this ice permanent */
4225 if (when
<= MAX_ICE_TIME
) {
4226 where
= ((long) x
<< 16) | (long) y
;
4227 (void) start_timer((long) when
, TIMER_LEVEL
, MELT_ICE_AWAY
,
4228 long_to_any(where
));
4235 * Called when ice has melted completely away.
4238 melt_ice_away(arg
, timeout
)
4240 long timeout UNUSED
;
4243 long where
= arg
->a_long
;
4245 y
= (xchar
) (where
& 0xFFFF);
4246 x
= (xchar
) ((where
>> 16) & 0xFFFF);
4247 /* melt_ice does newsym when appropriate */
4248 melt_ice(x
, y
, "Some ice melts away.");
4251 /* Burn floor scrolls, evaporate pools, etc... in a single square.
4252 * Used both for normal bolts of fire, cold, etc... and for fireballs.
4253 * Sets shopdamage to TRUE if a shop door is destroyed, and returns the
4254 * amount by which range is reduced (the latter is just ignored by fireballs)
4257 zap_over_floor(x
, y
, type
, shopdamage
, exploding_wand_typ
)
4260 boolean
*shopdamage
;
4261 short exploding_wand_typ
;
4263 const char *zapverb
;
4266 struct rm
*lev
= &levl
[x
][y
];
4267 boolean see_it
= cansee(x
, y
), yourzap
;
4268 int rangemod
= 0, abstype
= abs(type
) % 10;
4273 if (t
&& t
->ttyp
== WEB
) {
4274 /* a burning web is too flimsy to notice if you can't see it */
4276 Norep("A web bursts into flames!");
4277 (void) delfloortrap(t
);
4282 melt_ice(x
, y
, (char *) 0);
4283 } else if (is_pool(x
, y
)) {
4284 const char *msgtxt
= "You hear hissing gas.";
4286 if (lev
->typ
!= POOL
) { /* MOAT or DRAWBRIDGE_UP */
4288 msgtxt
= "Some water evaporates.";
4292 t
= maketrap(x
, y
, PIT
);
4296 msgtxt
= "The water evaporates.";
4298 Norep("%s", msgtxt
);
4299 if (lev
->typ
== ROOM
)
4301 } else if (IS_FOUNTAIN(lev
->typ
)) {
4303 pline("Steam billows from the fountain.");
4305 dryup(x
, y
, type
> 0);
4307 break; /* ZT_FIRE */
4310 if (is_pool(x
, y
) || is_lava(x
, y
)) {
4311 boolean lava
= is_lava(x
, y
),
4312 moat
= is_moat(x
, y
);
4314 if (lev
->typ
== WATER
) {
4315 /* For now, don't let WATER freeze. */
4317 pline_The("%s freezes for a moment.", hliquid("water"));
4319 You_hear("a soft crackling.");
4320 rangemod
-= 1000; /* stop */
4324 Strcpy(buf
, waterbody_name(x
, y
)); /* for MOAT */
4326 if (lev
->typ
== DRAWBRIDGE_UP
) {
4327 lev
->drawbridgemask
&= ~DB_UNDER
; /* clear lava */
4328 lev
->drawbridgemask
|= (lava
? DB_FLOOR
: DB_ICE
);
4331 lev
->icedpool
= (lev
->typ
== POOL
) ? ICED_POOL
4333 lev
->typ
= lava
? ROOM
: ICE
;
4338 Norep("The %s cools and solidifies.", hliquid("lava"));
4340 Norep("The %s is bridged with ice!", buf
);
4342 Norep("The %s freezes.", hliquid("water"));
4345 You_hear("a crackling sound.");
4347 if (x
== u
.ux
&& y
== u
.uy
) {
4348 if (u
.uinwater
) { /* not just `if (Underwater)' */
4349 /* leave the no longer existent water */
4353 vision_full_recalc
= 1;
4354 } else if (u
.utrap
&& u
.utraptype
== TT_LAVA
) {
4357 You("pass through the now-solid rock.");
4359 u
.utrap
= rn1(50, 20);
4360 u
.utraptype
= TT_INFLOOR
;
4361 You("are firmly stuck in the cooling rock.");
4364 } else if ((mon
= m_at(x
, y
)) != 0) {
4365 /* probably ought to do some hefty damage to any
4366 non-ice creature caught in freezing water;
4367 at a minimum, eels are forced out of hiding */
4368 if (is_swimmer(mon
->data
) && mon
->mundetected
) {
4369 mon
->mundetected
= 0;
4374 start_melt_ice_timeout(x
, y
, 0L);
4375 obj_ice_effects(x
, y
, TRUE
);
4379 } else if (is_ice(x
, y
)) {
4382 /* Already ice here, so just firm it up. */
4383 /* Now ensure that only ice that is already timed is affected */
4384 if ((melt_time
= spot_time_left(x
, y
, MELT_ICE_AWAY
)) != 0L) {
4385 spot_stop_timers(x
, y
, MELT_ICE_AWAY
);
4386 start_melt_ice_timeout(x
, y
, melt_time
);
4389 break; /* ZT_COLD */
4392 (void) create_gas_cloud(x
, y
, 1, 8);
4396 if (lev
->typ
== IRONBARS
) {
4397 if ((lev
->wall_info
& W_NONDIGGABLE
) != 0) {
4399 Norep("The %s corrode somewhat but remain intact.",
4400 defsyms
[S_bars
].explanation
);
4401 /* but nothing actually happens... */
4405 Norep("The %s melt.", defsyms
[S_bars
].explanation
);
4406 if (*in_rooms(x
, y
, SHOPBASE
)) {
4407 /* in case we ever have a shop bounded by bars */
4411 add_damage(x
, y
, (type
>= 0) ? 300L : 0L);
4416 lev
->doormask
= D_NODOOR
;
4422 break; /* ZT_ACID */
4428 /* set up zap text for possible door feedback; for exploding wand, we
4429 want "the blast" rather than "your blast" even if hero caused it */
4430 yourzap
= (type
>= 0 && !exploding_wand_typ
);
4431 zapverb
= "blast"; /* breath attack or wand explosion */
4432 if (!exploding_wand_typ
) {
4433 if (abs(type
) < ZT_SPELL(0))
4434 zapverb
= "bolt"; /* wand zap */
4435 else if (abs(type
) < ZT_BREATH(0))
4439 /* secret door gets revealed, converted into regular door */
4440 if (levl
[x
][y
].typ
== SDOOR
) {
4441 cvt_sdoor_to_door(&levl
[x
][y
]); /* .typ = DOOR */
4442 /* target spot will now pass closed_door() test below
4443 (except on rogue level) */
4446 pline("%s %s reveals a secret door.",
4447 yourzap
? "Your" : "The", zapverb
);
4448 else if (Is_rogue_level(&u
.uz
))
4449 draft_message(FALSE
); /* "You feel a draft." (open doorway) */
4452 /* regular door absorbs remaining zap range, possibly gets destroyed */
4453 if (closed_door(x
, y
)) {
4454 int new_doormask
= -1;
4455 const char *see_txt
= 0, *sense_txt
= 0, *hear_txt
= 0;
4460 new_doormask
= D_NODOOR
;
4461 see_txt
= "The door is consumed in flames!";
4462 sense_txt
= "smell smoke.";
4465 new_doormask
= D_NODOOR
;
4466 see_txt
= "The door freezes and shatters!";
4467 sense_txt
= "feel cold.";
4470 /* death spells/wands don't disintegrate */
4471 if (abs(type
) != ZT_BREATH(ZT_DEATH
))
4473 new_doormask
= D_NODOOR
;
4474 see_txt
= "The door disintegrates!";
4475 hear_txt
= "crashing wood.";
4478 new_doormask
= D_BROKEN
;
4479 see_txt
= "The door splinters!";
4480 hear_txt
= "crackling.";
4484 if (exploding_wand_typ
> 0) {
4485 /* Magical explosion from misc exploding wand */
4486 if (exploding_wand_typ
== WAN_STRIKING
) {
4487 new_doormask
= D_BROKEN
;
4488 see_txt
= "The door crashes open!";
4489 sense_txt
= "feel a burst of cool air.";
4494 /* "the door absorbs the blast" would be
4495 inaccurate for an exploding wand since
4496 other adjacent locations still get hit */
4497 if (exploding_wand_typ
)
4498 pline_The("door remains intact.");
4500 pline_The("door absorbs %s %s!", yourzap
? "your" : "the",
4503 You_feel("vibrations.");
4506 if (new_doormask
>= 0) { /* door gets broken */
4507 if (*in_rooms(x
, y
, SHOPBASE
)) {
4509 add_damage(x
, y
, 400L);
4511 } else /* caused by monster */
4512 add_damage(x
, y
, 0L);
4514 lev
->doormask
= new_doormask
;
4515 unblock_point(x
, y
); /* vision */
4519 } else if (sense_txt
) {
4521 } else if (hear_txt
)
4522 You_hear1(hear_txt
);
4523 if (picking_at(x
, y
)) {
4530 if (OBJ_AT(x
, y
) && abstype
== ZT_FIRE
)
4531 if (burn_floor_objects(x
, y
, FALSE
, type
> 0) && couldsee(x
, y
)) {
4533 You("%s of smoke.", !Blind
? "see a puff" : "smell a whiff");
4535 if ((mon
= m_at(x
, y
)) != 0) {
4536 /* Cannot use wakeup() which also angers the monster */
4542 if (mon
->ispriest
&& *in_rooms(mon
->mx
, mon
->my
, TEMPLE
))
4544 if (mon
->isshk
&& !*u
.ushops
)
4551 /* fractured by pick-axe or wand of striking */
4554 register struct obj
*obj
; /* no texts here! */
4557 boolean by_you
= !context
.mon_moving
;
4559 if (by_you
&& get_obj_location(obj
, &x
, &y
, 0) && costly_spot(x
, y
)) {
4560 struct monst
*shkp
= 0;
4561 char objroom
= *in_rooms(x
, y
, SHOPBASE
);
4563 if (billable(&shkp
, obj
, objroom
, FALSE
)) {
4564 /* shop message says "you owe <shk> <$> for it!" so we need
4565 to precede that with a message explaining what "it" is */
4566 You("fracture %s %s.", s_suffix(shkname(shkp
)), xname(obj
));
4567 breakobj(obj
, x
, y
, TRUE
, FALSE
); /* charges for shop goods */
4570 if (by_you
&& obj
->otyp
== BOULDER
)
4574 obj
->oclass
= GEM_CLASS
;
4575 obj
->quan
= (long) rn1(60, 7);
4576 obj
->owt
= weight(obj
);
4577 obj
->dknown
= obj
->bknown
= obj
->rknown
= 0;
4578 obj
->known
= objects
[obj
->otyp
].oc_uses_known
? 0 : 1;
4579 dealloc_oextra(obj
);
4581 if (obj
->where
== OBJ_FLOOR
) {
4582 obj_extract_self(obj
); /* move rocks back on top */
4583 place_object(obj
, obj
->ox
, obj
->oy
);
4584 if (!does_block(obj
->ox
, obj
->oy
, &levl
[obj
->ox
][obj
->oy
]))
4585 unblock_point(obj
->ox
, obj
->oy
);
4586 if (cansee(obj
->ox
, obj
->oy
))
4587 newsym(obj
->ox
, obj
->oy
);
4591 /* handle statue hit by striking/force bolt/pick-axe */
4594 register struct obj
*obj
;
4596 /* [obj is assumed to be on floor, so no get_obj_location() needed] */
4597 struct trap
*trap
= t_at(obj
->ox
, obj
->oy
);
4599 boolean by_you
= !context
.mon_moving
;
4601 if (trap
&& trap
->ttyp
== STATUE_TRAP
4602 && activate_statue_trap(trap
, obj
->ox
, obj
->oy
, TRUE
))
4604 /* drop any objects contained inside the statue */
4605 while ((item
= obj
->cobj
) != 0) {
4606 obj_extract_self(item
);
4607 place_object(item
, obj
->ox
, obj
->oy
);
4609 if (by_you
&& Role_if(PM_ARCHEOLOGIST
) && (obj
->spe
& STATUE_HISTORIC
)) {
4610 You_feel("guilty about damaging such a historic statue.");
4619 * destroy_strings[dindx][0:singular,1:plural,2:killer_reason]
4620 * [0] freezing potion
4621 * [1] boiling potion other than oil
4622 * [2] boiling potion of oil
4623 * [3] burning scroll
4624 * [4] burning spellbook
4627 * (books, rings, and wands don't stack so don't need plural form;
4628 * crumbling ring doesn't do damage so doesn't need killer reason)
4630 const char *const destroy_strings
[][3] = {
4631 /* also used in trap.c */
4632 { "freezes and shatters", "freeze and shatter", "shattered potion" },
4633 { "boils and explodes", "boil and explode", "boiling potion" },
4634 { "ignites and explodes", "ignite and explode", "exploding potion" },
4635 { "catches fire and burns", "catch fire and burn", "burning scroll" },
4636 { "catches fire and burns", "", "burning book" },
4637 { "turns to dust and vanishes", "", "" },
4638 { "breaks apart and explodes", "", "exploding wand" },
4642 destroy_item(osym
, dmgtyp
)
4643 register int osym
, dmgtyp
;
4645 register struct obj
*obj
, *obj2
;
4646 int dmg
, xresist
, skip
;
4650 boolean physical_damage
;
4652 for (obj
= invent
; obj
; obj
= obj2
) {
4654 physical_damage
= FALSE
;
4655 if (obj
->oclass
!= osym
)
4656 continue; /* test only objs of type osym */
4658 continue; /* don't destroy artifacts */
4659 if (obj
->in_use
&& obj
->quan
== 1L)
4660 continue; /* not available */
4662 /* lint suppression */
4668 if (osym
== POTION_CLASS
&& obj
->otyp
!= POT_OIL
) {
4676 xresist
= (Fire_resistance
&& obj
->oclass
!= POTION_CLASS
4677 && obj
->otyp
!= GLOB_OF_GREEN_SLIME
);
4679 if (obj
->otyp
== SCR_FIRE
|| obj
->otyp
== SPE_FIREBALL
)
4681 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
4684 pline("%s glows a strange %s, but remains intact.",
4685 The(xname(obj
)), hcolor("dark red"));
4690 dindx
= (obj
->otyp
!= POT_OIL
) ? 1 : 2;
4702 if (obj
->otyp
== GLOB_OF_GREEN_SLIME
) {
4703 dindx
= 1; /* boil and explode */
4704 dmg
= (obj
->owt
+ 19) / 20;
4715 xresist
= (Shock_resistance
&& obj
->oclass
!= RING_CLASS
);
4719 if (obj
->otyp
== RIN_SHOCK_RESISTANCE
) {
4727 if (obj
->otyp
== WAN_LIGHTNING
) {
4732 if (obj
== current_wand
) { skip
++; break; }
4748 --quan
; /* one will be used up elsewhere */
4749 for (i
= cnt
= 0L; i
< quan
; i
++)
4756 ? (quan
== 1L) ? "Your" /* 1 of 1 */
4757 : "One of your" /* 1 of N */
4758 : (cnt
< quan
) ? "Some of your" /* n of N */
4759 : (quan
== 2L) ? "Both of your" /* 2 of 2 */
4760 : "All of your"; /* N of N */
4761 pline("%s %s %s!", mult
, xname(obj
),
4762 destroy_strings
[dindx
][(cnt
> 1L)]);
4763 if (osym
== POTION_CLASS
&& dmgtyp
!= AD_COLD
) {
4764 if (!breathless(youmonst
.data
) || haseyes(youmonst
.data
))
4767 if (obj
->owornmask
) {
4768 if (obj
->owornmask
& W_RING
) /* ring being worn */
4773 if (obj
== current_wand
)
4774 current_wand
= 0; /* destroyed */
4775 for (i
= 0; i
< cnt
; i
++)
4779 You("aren't hurt!");
4781 const char *how
= destroy_strings
[dindx
][2];
4782 boolean one
= (cnt
== 1L);
4784 if (dmgtyp
== AD_FIRE
&& osym
== FOOD_CLASS
)
4785 how
= "exploding glob of slime";
4786 if (physical_damage
)
4787 dmg
= Maybe_Half_Phys(dmg
);
4788 losehp(dmg
, one
? how
: (const char *) makeplural(how
),
4789 one
? KILLED_BY_AN
: KILLED_BY
);
4790 exercise(A_STR
, FALSE
);
4799 destroy_mitem(mtmp
, osym
, dmgtyp
)
4803 struct obj
*obj
, *obj2
;
4809 if (mtmp
== &youmonst
) { /* this simplifies artifact_hit() */
4810 destroy_item(osym
, dmgtyp
);
4811 return 0; /* arbitrary; value doesn't matter to artifact_hit() */
4814 vis
= canseemon(mtmp
);
4815 for (obj
= mtmp
->minvent
; obj
; obj
= obj2
) {
4817 if (obj
->oclass
!= osym
)
4818 continue; /* test only objs of type osym */
4825 if (osym
== POTION_CLASS
&& obj
->otyp
!= POT_OIL
) {
4833 if (obj
->otyp
== SCR_FIRE
|| obj
->otyp
== SPE_FIREBALL
)
4835 if (obj
->otyp
== SPE_BOOK_OF_THE_DEAD
) {
4838 pline("%s glows a strange %s, but remains intact.",
4839 The(distant_name(obj
, xname
)), hcolor("dark red"));
4844 dindx
= (obj
->otyp
!= POT_OIL
) ? 1 : 2;
4856 if (obj
->otyp
== GLOB_OF_GREEN_SLIME
) {
4857 dindx
= 1; /* boil and explode */
4858 tmp
+= (obj
->owt
+ 19) / 20;
4872 if (obj
->otyp
== RIN_SHOCK_RESISTANCE
) {
4879 if (obj
->otyp
== WAN_LIGHTNING
) {
4896 for (i
= cnt
= 0L; i
< quan
; i
++)
4904 (cnt
== obj
->quan
) ? "" : (cnt
> 1L) ? "Some of "
4906 (cnt
== obj
->quan
) ? Yname2(obj
) : yname(obj
),
4907 destroy_strings
[dindx
][(cnt
> 1L)]);
4908 for (i
= 0; i
< cnt
; i
++)
4916 resist(mtmp
, oclass
, damage
, tell
)
4931 break; /* instrument */
4934 break; /* artifact */
4949 dlev
= (int) mtmp
->m_lev
;
4953 dlev
= is_mplayer(mtmp
->data
) ? u
.ulevel
: 1;
4955 resisted
= rn2(100 + alev
- dlev
) < mtmp
->data
->mr
;
4958 shieldeff(mtmp
->mx
, mtmp
->my
);
4959 pline("%s resists!", Monnam(mtmp
));
4961 damage
= (damage
+ 1) / 2;
4965 mtmp
->mhp
-= damage
;
4966 if (mtmp
->mhp
< 1) {
4968 monkilled(mtmp
, "", AD_RBRE
);
4976 #define MAXWISHTRY 5
4979 wishcmdassist(triesleft
)
4982 static NEARDATA
const char *
4986 "Enter the name of an object, such as \"potion of monster detection\",",
4987 "\"scroll labeled README\", \"elven mithril-coat\", or \"Grimtooth\"",
4988 "(without the quotes).",
4990 "For object types which come in stacks, you may specify a plural name",
4991 "such as \"potions of healing\", or specify a count, such as \"1000 gold",
4992 "pieces\", although that aspect of your wish might not be granted.",
4994 "You may also specify various prefix values which might be used to",
4995 "modify the item, such as \"uncursed\" or \"rustproof\" or \"+1\".",
4996 "Most modifiers shown when viewing your inventory can be specified.",
4998 "You may specify 'nothing' to explicitly decline this wish.",
5001 preserve_wishless
[] = "Doing so will preserve 'wishless' conduct.",
5003 "If you specify an unrecognized object name %s%s time%s,",
5004 retry_too
[] = "a randomly chosen item will be granted.",
5005 suppress_cmdassist
[] =
5006 "(Suppress this assistance with !cmdassist in your config file.)",
5007 *cardinals
[] = { "zero", "one", "two", "three", "four", "five" },
5008 too_many
[] = "too many";
5013 win
= create_nhwindow(NHW_TEXT
);
5016 for (i
= 0; i
< SIZE(wishinfo
) - 1; ++i
)
5017 putstr(win
, 0, wishinfo
[i
]);
5018 if (!u
.uconduct
.wishes
)
5019 putstr(win
, 0, preserve_wishless
);
5021 Sprintf(buf
, retry_info
,
5022 (triesleft
>= 0 && triesleft
< SIZE(cardinals
))
5023 ? cardinals
[triesleft
]
5025 (triesleft
< MAXWISHTRY
) ? " more" : "",
5027 putstr(win
, 0, buf
);
5028 putstr(win
, 0, retry_too
);
5030 if (iflags
.cmdassist
)
5031 putstr(win
, 0, suppress_cmdassist
);
5032 display_nhwindow(win
, FALSE
);
5033 destroy_nhwindow(win
);
5039 char buf
[BUFSZ
], promptbuf
[BUFSZ
];
5040 struct obj
*otmp
, nothing
;
5043 promptbuf
[0] = '\0';
5044 nothing
= zeroobj
; /* lint suppression; only its address matters */
5046 You("may wish for an object.");
5048 Strcpy(promptbuf
, "For what do you wish");
5049 if (iflags
.cmdassist
&& tries
> 0)
5050 Strcat(promptbuf
, " (enter 'help' for assistance)");
5051 Strcat(promptbuf
, "?");
5052 getlin(promptbuf
, buf
);
5053 (void) mungspaces(buf
);
5054 if (buf
[0] == '\033') {
5056 } else if (!strcmpi(buf
, "help")) {
5057 wishcmdassist(MAXWISHTRY
- tries
);
5061 * Note: if they wished for and got a non-object successfully,
5062 * otmp == &zeroobj. That includes gold, or an artifact that
5063 * has been denied. Wishing for "nothing" requires a separate
5064 * value to remain distinct.
5066 otmp
= readobjnam(buf
, ¬hing
);
5068 pline("Nothing fitting that description exists in the game.");
5069 if (++tries
< MAXWISHTRY
)
5071 pline1(thats_enough_tries
);
5072 otmp
= readobjnam((char *) 0, (struct obj
*) 0);
5074 return; /* for safety; should never happen */
5075 } else if (otmp
== ¬hing
) {
5076 /* explicitly wished for "nothing", presumably attempting
5077 to retain wishless conduct */
5082 u
.uconduct
.wishes
++;
5084 if (otmp
!= &zeroobj
) {
5086 *verb
= ((Is_airlevel(&u
.uz
) || u
.uinwater
) ? "slip" : "drop"),
5087 *oops_msg
= (u
.uswallow
5088 ? "Oops! %s out of your reach!"
5089 : (Is_airlevel(&u
.uz
) || Is_waterlevel(&u
.uz
)
5090 || levl
[u
.ux
][u
.uy
].typ
< IRONBARS
5091 || levl
[u
.ux
][u
.uy
].typ
>= ICE
)
5092 ? "Oops! %s away from you!"
5093 : "Oops! %s to the floor!");
5095 /* The(aobjnam()) is safe since otmp is unidentified -dlc */
5096 (void) hold_another_object(otmp
, oops_msg
,
5097 The(aobjnam(otmp
, verb
)),
5099 u
.ublesscnt
+= rn1(100, 50); /* the gods take notice */