make rank() static again
[NetHack.git] / src / artifact.c
blob506d50ea1e969728922fc1a981a29c6e2d709189
1 /* NetHack 3.7 artifact.c $NHDT-Date: 1715889721 2024/05/16 20:02:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.236 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2013. */
4 /* NetHack may be freely redistributed. See license for details. */
6 #include "hack.h"
7 #include "artifact.h"
8 #include "artilist.h"
11 * Note: both artilist[] and artiexist[] have a dummy element #0,
12 * so loops over them should normally start at #1. The primary
13 * exception is the save & restore code, which doesn't care about
14 * the contents, just the total size.
17 staticfn struct artifact *get_artifact(struct obj *) NONNULL;
19 /* #define get_artifact(o) \
20 (((o) && ((o)->artifact > 0 && (o)->artifact < AFTER_LAST_ARTIFACT)) \
21 ? &artilist[(int) (o)->oartifact] \
22 : &artilist[ART_NONARTIFACT]) */
24 staticfn boolean bane_applies(const struct artifact *, struct monst *)
25 NONNULLARG12;
26 staticfn int spec_applies(const struct artifact *, struct monst *)
27 NONNULLARG12;
28 staticfn int invoke_ok(struct obj *);
29 staticfn void nothing_special(struct obj *) NONNULLARG1;
30 staticfn int arti_invoke(struct obj *);
31 staticfn boolean Mb_hit(struct monst * magr, struct monst *mdef,
32 struct obj *, int *, int, boolean, char *);
33 staticfn unsigned long abil_to_spfx(long *) NONNULLARG1;
34 staticfn uchar abil_to_adtyp(long *) NONNULLARG1;
35 staticfn int glow_strength(int);
36 staticfn boolean untouchable(struct obj *, boolean);
37 staticfn int count_surround_traps(coordxy, coordxy);
38 staticfn void dispose_of_orig_obj(struct obj *);
40 /* The amount added to the victim's total hit points to insure that the
41 victim will be killed even after damage bonus/penalty adjustments.
42 Most such penalties are small, and 200 is plenty; the exception is
43 half physical damage. 3.3.1 and previous versions tried to use a very
44 large number to account for this case; now, we just compute the fatal
45 damage by adding it to 2 times the total hit points instead of 1 time.
46 Note: this will still break if they have more than about half the number
47 of hit points that will fit in a 15 bit integer. */
48 #define FATAL_DAMAGE_MODIFIER 200
50 /* artifact tracking; gift and wish imply found; it also gets set for items
51 seen on the floor, in containers, and wielded or dropped by monsters */
52 struct arti_info {
53 Bitfield(exists, 1); /* 1 if corresponding artifact has been created */
54 Bitfield(found, 1); /* 1 if artifact is known by hero to exist */
55 Bitfield(gift, 1); /* 1 iff artifact was created as a prayer reward */
56 Bitfield(wish, 1); /* 1 iff artifact was created via wish */
57 Bitfield(named, 1); /* 1 iff artifact was made by naming an item */
58 Bitfield(viadip, 1); /* 1 iff dipped long sword became Excalibur */
59 Bitfield(lvldef, 1); /* 1 iff created by special level definition */
60 Bitfield(bones, 1); /* 1 iff came from bones file */
61 Bitfield(rndm, 1); /* 1 iff randomly generated */
63 /* array of flags tracking which artifacts exist, indexed by ART_xx;
64 ART_xx values are 1..N, element [0] isn't used; no terminator needed */
65 static struct arti_info artiexist[1 + NROFARTIFACTS];
66 /* discovery list; for N discovered artifacts, the first N entries are ART_xx
67 values in discovery order, the remaining (NROFARTIFACTS-N) slots are 0 */
68 static xint16 artidisco[NROFARTIFACTS];
69 /* note: artiexist[] and artidisco[] don't need to be in struct g; they
70 * get explicitly initialized at game start so don't need to be part of
71 * bulk re-init if game restart ever gets implemented. They are saved
72 * and restored but that is done through this file so they can be local.
74 static const struct arti_info zero_artiexist = {0}; /* all bits zero */
76 staticfn void hack_artifacts(void);
78 /* handle some special cases; must be called after u_init() */
79 staticfn void
80 hack_artifacts(void)
82 struct artifact *art;
83 int alignmnt = aligns[flags.initalign].value;
85 /* Fix up the alignments of "gift" artifacts */
86 for (art = artilist + 1; art->otyp; art++)
87 if (art->role == Role_switch && art->alignment != A_NONE)
88 art->alignment = alignmnt;
90 /* Excalibur can be used by any lawful character, not just knights */
91 if (!Role_if(PM_KNIGHT))
92 artilist[ART_EXCALIBUR].role = NON_PM;
94 /* Fix up the quest artifact */
95 if (gu.urole.questarti) {
96 artilist[gu.urole.questarti].alignment = alignmnt;
97 artilist[gu.urole.questarti].role = Role_switch;
99 return;
102 /* zero out the artifact existence list */
103 void
104 init_artifacts(void)
106 (void) memset((genericptr_t) artiexist, 0, sizeof artiexist);
107 (void) memset((genericptr_t) artidisco, 0, sizeof artidisco);
108 hack_artifacts();
111 void
112 save_artifacts(NHFILE *nhfp)
114 if (nhfp->structlevel) {
115 bwrite(nhfp->fd, (genericptr_t) artiexist, sizeof artiexist);
116 bwrite(nhfp->fd, (genericptr_t) artidisco, sizeof artidisco);
120 void
121 restore_artifacts(NHFILE *nhfp)
123 if (nhfp->structlevel) {
124 mread(nhfp->fd, (genericptr_t) artiexist, sizeof artiexist);
125 mread(nhfp->fd, (genericptr_t) artidisco, sizeof artidisco);
127 hack_artifacts(); /* redo non-saved special cases */
130 const char *
131 artiname(int artinum)
133 if (artinum <= 0 || artinum > NROFARTIFACTS)
134 return "";
135 return artilist[artinum].name;
139 Make an artifact. If a specific alignment is specified, then an object of
140 the appropriate alignment is created from scratch, or 0 is returned if
141 none is available. (If at least one aligned artifact has already been
142 given, then unaligned ones also become eligible for this.)
143 If no alignment is given, then 'otmp' is converted
144 into an artifact of matching type, or returned as-is if that's not
145 possible.
146 For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE, 99);''
147 For the 1st, ``obj = mk_artifact((struct obj *) 0, some_alignment, ...);''.
148 The max_giftvalue is the value of the sacrifice, for an artifact obtained
149 by sacrificing, or 99 otherwise.
151 struct obj *
152 mk_artifact(
153 struct obj *otmp, /* existing object; ignored and disposed of
154 * if alignment specified */
155 aligntyp alignment, /* target alignment, or A_NONE */
156 uchar max_giftvalue, /* cap on generated giftvalue */
157 boolean adjust_spe) /* whether to add spe to situational artifacts */
159 const struct artifact *a;
160 int m, n, altn;
161 boolean by_align = (alignment != A_NONE);
162 short o_typ = (by_align || !otmp) ? 0 : otmp->otyp;
163 boolean unique = !by_align && otmp && objects[o_typ].oc_unique;
164 short eligible[NROFARTIFACTS];
165 xint16 skill_compatibility;
167 n = altn = 0; /* no candidates found yet */
168 eligible[0] = 0; /* lint suppression */
169 /* gather eligible artifacts */
170 for (m = 1, a = &artilist[m]; a->otyp; a++, m++) {
171 if (artiexist[m].exists)
172 continue;
173 if ((a->spfx & SPFX_NOGEN) || unique)
174 continue;
175 if (a->gift_value > max_giftvalue && !Role_if(a->role))
176 continue;
178 if (!by_align) {
179 /* looking for a particular type of item; not producing a
180 divine gift so we don't care about role's first choice */
181 if (a->otyp == o_typ)
182 eligible[n++] = m;
183 continue; /* move on to next possibility */
186 /* we're looking for an alignment-specific item
187 suitable for hero's role+race */
188 if ((a->alignment == alignment || a->alignment == A_NONE)
189 /* avoid enemies' equipment */
190 && (a->race == NON_PM || !race_hostile(&mons[a->race]))) {
191 /* when a role-specific first choice is available, use it */
192 if (Role_if(a->role)) {
193 /* make this be the only possibility in the list */
194 eligible[0] = m;
195 n = 1;
196 break; /* skip all other candidates */
199 /* check if this is skill-compatible */
200 skill_compatibility = P_SKILLED;
201 if (objects[a->otyp].oc_class == WEAPON_CLASS) {
202 schar skill = objects[a->otyp].oc_skill;
203 if (skill < 0)
204 skill_compatibility = P_MAX_SKILL(-skill);
205 else
206 skill_compatibility = P_MAX_SKILL(skill);
209 /* found something to consider for random selection */
210 if ((a->alignment != A_NONE || u.ugifts > 0 || !rn2(3)) &&
211 (!rn2(4) || skill_compatibility >= P_SKILLED ||
212 (skill_compatibility >= P_BASIC && rn2(2)))) {
213 /* right alignment, or non-aligned with at least 1
214 previous gift bestowed, makes this one viable;
215 unaligned artifacts are possible even as the first
216 gift, but less likely; if it's a bad weapon type
217 for the role that also makes it less likely */
218 eligible[n++] = m;
219 } else {
220 /* if no candidates have been found yet, record
221 this one as a[nother] fallback possibility in
222 case all aligned candidates have been used up
223 (via wishing, naming, bones, random generation)
224 or failed the randomized compatibility checks */
225 if (!n)
226 eligible[altn++] = m;
227 /* [once a regular candidate is found, the list
228 is overwritten and `altn' becomes irrelevant] */
233 /* resort to fallback list if main list was empty */
234 if (!n)
235 n = altn;
237 if (n) {
238 /* found at least one candidate; pick one at random */
239 m = eligible[rn2(n)]; /* [0..n-1] */
240 a = &artilist[m];
242 /* make an appropriate object if necessary, then christen it */
243 if (by_align) {
244 /* 'by_align' indicates that an alignment was passed as
245 * an argument, but also that the 'otmp' argument is not
246 * relevant */
247 struct obj *artiobj = mksobj((int) a->otyp, TRUE, FALSE);
249 /* nonnull value of 'otmp' is unexpected. Cope. */
250 if (otmp) /* just in case; avoid orphaning */
251 dispose_of_orig_obj(otmp);
252 otmp = artiobj;
255 * otmp should be nonnull at this point:
256 * either the passed argument (if !by_align == A_NONE), or
257 * the result of mksobj() just above if by_align is an alignment. */
258 assert(otmp != 0);
259 /* prevent erosion from generating */
260 otmp->oeroded = otmp->oeroded2 = 0;
261 otmp = oname(otmp, a->name, ONAME_NO_FLAGS);
262 otmp->oartifact = m; /* probably already set by this point, but */
263 /* set existence and reason for creation bits */
264 artifact_origin(otmp, ONAME_RANDOM); /* 'random' is default */
265 if (adjust_spe) {
266 int new_spe;
268 /* Adjust artiobj->spe by a->gen_spe. (This is a no-op for
269 non-weapons, which always have a gen_spe of 0, and for many
270 weapons, too.) The result is clamped into the "normal" range to
271 prevent an outside chance of +12 artifacts generating. */
272 new_spe = (int)otmp->spe + a->gen_spe;
273 if (new_spe >= -10 && new_spe < 10)
274 otmp->spe = new_spe;
276 } else {
277 /* nothing appropriate could be found; return original object */
278 if (by_align && otmp) {
279 /* (there shouldn't have been an original object). Deal with it.
280 * The callers that passed an alignment and a NULL otmp are
281 * prepared to get a potential NULL return value, so this is okay */
282 dispose_of_orig_obj(otmp);
283 otmp = 0;
284 } /* otherwise, otmp has not changed; just fallthrough to return it */
286 return otmp;
289 staticfn void
290 dispose_of_orig_obj(struct obj *obj)
292 if (!obj)
293 return;
295 obj_extract_self(obj);
296 obfree(obj, (struct obj *) 0);
300 * Returns the full name (with articles and correct capitalization) of an
301 * artifact named "name" if one exists, or NULL, it not.
302 * The given name must be rather close to the real name for it to match.
303 * The object type of the artifact is returned in otyp if the return value
304 * is non-NULL.
306 const char *
307 artifact_name(
308 const char *name, /* string from player that might be an artifact name */
309 short *otyp_p, /* secondary output */
310 boolean fuzzy) /* whether to allow extra or omitted spaces or dashes */
312 const struct artifact *a;
313 const char *aname;
315 if (!strncmpi(name, "the ", 4))
316 name += 4;
318 for (a = artilist + 1; a->otyp; a++) {
319 aname = a->name;
320 if (!strncmpi(aname, "the ", 4))
321 aname += 4;
322 if (!fuzzy ? !strcmpi(name, aname)
323 : fuzzymatch(name, aname, " -", TRUE)) {
324 if (otyp_p)
325 *otyp_p = a->otyp;
326 return a->name;
330 return (char *) 0;
333 boolean
334 exist_artifact(int otyp, const char *name)
336 const struct artifact *a;
337 struct arti_info *arex;
339 if (otyp && *name)
340 for (a = artilist + 1, arex = artiexist + 1; a->otyp; a++, arex++)
341 if ((int) a->otyp == otyp && !strcmp(a->name, name))
342 return arex->exists ? TRUE : FALSE;
343 return FALSE;
346 /* an artifact has just been created or is being "un-created" for a chance
347 to be created again later */
348 void
349 artifact_exists(
350 struct obj *otmp,
351 const char *name,
352 boolean mod, /* True: exists, False: being un-created */
353 unsigned flgs) /* ONAME_xyz flags; not relevant if !mod */
355 const struct artifact *a;
357 if (otmp && *name)
358 for (a = artilist + 1; a->otyp; a++)
359 if (a->otyp == otmp->otyp && !strcmp(a->name, name)) {
360 int m = (int) (a - artilist);
362 otmp->oartifact = (char) (mod ? m : 0);
363 otmp->age = 0;
364 if (otmp->otyp == RIN_INCREASE_DAMAGE)
365 otmp->spe = 0;
366 if (mod) { /* means being created rather than un-created */
367 /* one--and only one--of these should always be set */
368 if ((flgs & (ONAME_VIA_NAMING | ONAME_WISH | ONAME_GIFT
369 | ONAME_VIA_DIP | ONAME_LEVEL_DEF
370 | ONAME_BONES | ONAME_RANDOM)) == 0)
371 flgs |= ONAME_RANDOM; /* the default origin */
372 /* 'exists' bit will become set (in artifact_origin();
373 there's no ONAME_ flag) and flgs might also contain
374 the know_arti bit (hero knows that artifact exists) */
375 artifact_origin(otmp, flgs);
376 } else { /* uncreate */
377 /* clear all the flag bits */
378 artiexist[m] = zero_artiexist;
380 break;
382 return;
385 /* mark an artifact as 'found' */
386 void
387 found_artifact(int a)
389 if (a < 1 || a > NROFARTIFACTS)
390 impossible("found_artifact: invalid artifact index! (%d)", a);
391 else if (!artiexist[a].exists)
392 impossible("found_artifact: artifact doesn't exist yet? (%d)", a);
393 else
394 artiexist[a].found = 1;
397 /* if an artifact hasn't already been designated 'found', do that now
398 and generate a livelog event about finding it */
399 void
400 find_artifact(struct obj *otmp)
402 int a = otmp->oartifact;
404 if (a && !artiexist[a].found) {
405 const char *where;
407 found_artifact(a); /* artiexist[a].found = 1 */
409 * Unlike costly_spot(), inside_shop() includes the "free spot"
410 * in front of the door. And it doesn't care whether or not
411 * there is a shopkeeper present.
413 * If hero sees a monster pick up a not-yet-found artifact, it
414 * will have its dknown flag set even if far away and will be
415 * described as 'found on the floor'. Similarly for dropping
416 * (possibly upon monster's death), dknown will be set and the
417 * artifact will be described as 'carried by a monster'.
418 * That's handled by caller: dog_invent(), mpickstuff(), or
419 * mdrop_obj() so that we get called before obj->where changes.
421 where = ((otmp->where == OBJ_FLOOR)
422 ? ((inside_shop(otmp->ox, otmp->oy) != NO_ROOM)
423 ? " in a shop"
424 : " on the floor")
425 /* artifacts aren't created in containers but could be
426 inside one if it comes from a bones level */
427 : (otmp->where == OBJ_CONTAINED) ? " in a container"
428 /* perhaps probing, or seeing monster wield artifact */
429 : (otmp->where == OBJ_MINVENT) ? " carried by a monster"
430 /* catchall: probably in inventory, picked up while
431 blind but now seen; there's no previous_where to
432 figure out how it got here */
433 : "");
434 livelog_printf(LL_ARTIFACT, "found %s%s",
435 bare_artifactname(otmp), where);
440 nartifact_exist(void)
442 int i, a = 0;
444 for (i = 1; i <= NROFARTIFACTS; ++i)
445 if (artiexist[i].exists)
446 ++a;
448 return a;
451 /* set artifact tracking flags;
452 calling sequence: oname() -> artifact_exists() -> artifact_origin() or
453 mksobj(),others -> mk_artifact() -> artifact_origin(random) possibly
454 followed by mksobj(),others -> artifact_origin(non-random origin) */
455 void
456 artifact_origin(
457 struct obj *arti, /* new artifact */
458 unsigned aflags) /* ONAME_xxx flags, shared by artifact_exists() */
460 int ct, a = arti->oartifact;
462 if (a) {
463 /* start by clearing all bits; most are mutually exclusive */
464 artiexist[a] = zero_artiexist;
465 /* set 'exists' bit back on; not specified via flag bit in aflags */
466 artiexist[a].exists = 1;
467 /* 'hero knows it exists' is expected for wish, gift, viadip, or
468 named and could eventually become set for any of the others */
469 if ((aflags & ONAME_KNOW_ARTI) != 0)
470 artiexist[a].found = 1;
471 /* should be exactly one of wish, gift, via_dip, via_naming,
472 level_def (quest), bones, and random (floor or monst's minvent) */
473 ct = 0;
474 if ((aflags & ONAME_WISH) != 0)
475 artiexist[a].wish = 1, ++ct;
476 if ((aflags & ONAME_GIFT) != 0)
477 artiexist[a].gift = 1, ++ct;
478 if ((aflags & ONAME_VIA_DIP) != 0)
479 artiexist[a].viadip = 1, ++ct;
480 if ((aflags & ONAME_VIA_NAMING) != 0)
481 artiexist[a].named = 1, ++ct;
482 if ((aflags & ONAME_LEVEL_DEF) != 0)
483 artiexist[a].lvldef = 1, ++ct;
484 if ((aflags & ONAME_BONES) != 0)
485 artiexist[a].bones = 1, ++ct;
486 if ((aflags & ONAME_RANDOM) != 0)
487 artiexist[a].rndm = 1, ++ct;
488 if (ct != 1)
489 impossible("invalid artifact origin: %4o", aflags);
493 boolean
494 spec_ability(struct obj *otmp, unsigned long abil)
496 const struct artifact *arti = get_artifact(otmp);
498 return (boolean) (arti != &artilist[ART_NONARTIFACT]
499 && (arti->spfx & abil) != 0L);
502 /* used so that callers don't need to known about SPFX_ codes */
503 boolean
504 confers_luck(struct obj *obj)
506 /* might as well check for this too */
507 if (obj->otyp == LUCKSTONE)
508 return TRUE;
510 return (boolean) (obj->oartifact && spec_ability(obj, SPFX_LUCK));
513 /* used to check whether a monster is getting reflection from an artifact */
514 boolean
515 arti_reflects(struct obj *obj)
517 const struct artifact *arti = get_artifact(obj);
519 if (arti != &artilist[ART_NONARTIFACT]) {
520 /* while being worn */
521 if ((obj->owornmask & ~W_ART) && (arti->spfx & SPFX_REFLECT))
522 return TRUE;
523 /* just being carried */
524 if (arti->cspfx & SPFX_REFLECT)
525 return TRUE;
527 return FALSE;
530 /* decide whether this obj is effective when attacking against shades;
531 does not consider the bonus for blessed objects versus undead */
532 boolean
533 shade_glare(struct obj *obj)
535 const struct artifact *arti;
537 /* any silver object is effective */
538 if (objects[obj->otyp].oc_material == SILVER)
539 return TRUE;
540 /* non-silver artifacts with bonus against undead also are effective */
541 arti = get_artifact(obj);
542 if (arti != &artilist[ART_NONARTIFACT] && (arti->spfx & SPFX_DFLAG2)
543 && arti->mtype == M2_UNDEAD)
544 return TRUE;
545 /* [if there was anything with special bonus against noncorporeals,
546 it would be effective too] */
547 /* otherwise, harmless to shades */
548 return FALSE;
551 /* returns 1 if name is restricted for otmp->otyp */
552 boolean
553 restrict_name(struct obj *otmp, const char *name)
555 const struct artifact *a;
556 const char *aname, *odesc, *other;
557 boolean sametype[NUM_OBJECTS];
558 int i, lo, hi, otyp = otmp->otyp, ocls = objects[otyp].oc_class;
560 if (!*name)
561 return FALSE;
562 if (!strncmpi(name, "the ", 4))
563 name += 4;
565 /* decide what types of objects are the same as otyp;
566 if it's been discovered, then only itself matches;
567 otherwise, include all other undiscovered objects
568 of the same class which have the same description
569 or share the same pool of shuffled descriptions */
570 (void) memset((genericptr_t) sametype, 0, sizeof sametype); /* FALSE */
571 sametype[otyp] = TRUE;
572 if (!objects[otyp].oc_name_known
573 && (odesc = OBJ_DESCR(objects[otyp])) != 0) {
574 obj_shuffle_range(otyp, &lo, &hi);
575 for (i = svb.bases[ocls]; i < NUM_OBJECTS; i++) {
576 if (objects[i].oc_class != ocls)
577 break;
578 if (!objects[i].oc_name_known
579 && (other = OBJ_DESCR(objects[i])) != 0
580 && (!strcmp(odesc, other) || (i >= lo && i <= hi)))
581 sametype[i] = TRUE;
585 /* Since almost every artifact is SPFX_RESTR, it doesn't cost
586 us much to do the string comparison before the spfx check.
587 Bug fix: don't name multiple elven daggers "Sting".
589 for (a = artilist + 1; a->otyp; a++) {
590 if (!sametype[a->otyp])
591 continue;
592 aname = a->name;
593 if (!strncmpi(aname, "the ", 4))
594 aname += 4;
595 if (!strcmp(aname, name))
596 return (boolean) ((a->spfx & (SPFX_NOGEN | SPFX_RESTR)) != 0
597 || otmp->quan > 1L);
600 return FALSE;
603 boolean
604 attacks(int adtyp, struct obj *otmp)
606 const struct artifact *weap;
608 if ((weap = get_artifact(otmp)) != &artilist[ART_NONARTIFACT])
609 return (boolean) (weap->attk.adtyp == adtyp);
610 return FALSE;
613 boolean
614 defends(int adtyp, struct obj *otmp)
616 const struct artifact *weap;
618 if (!otmp)
619 return FALSE;
620 if ((weap = get_artifact(otmp)) != &artilist[ART_NONARTIFACT])
621 return (boolean) (weap->defn.adtyp == adtyp);
622 if (Is_dragon_armor(otmp)) {
623 int otyp = otmp->otyp;
625 /* convert mail to scales to simplify testing */
626 if (Is_dragon_mail(otmp))
627 otyp += GRAY_DRAGON_SCALES - GRAY_DRAGON_SCALE_MAIL;
629 switch (adtyp) {
630 case AD_MAGM: /* magic missiles => general magic resistance */
631 return (otyp == GRAY_DRAGON_SCALES);
632 case AD_HALU: /* confers hallucination resistance */
633 return (otyp == GOLD_DRAGON_SCALES);
634 case AD_FIRE:
635 /*case AD_BLND: -- gives infravision but does not prevent blindness */
636 return (otyp == RED_DRAGON_SCALES); /* red but not gold */
637 case AD_COLD:
638 /*case AD_FAMN: -- slows digestion but does not override Famine */
639 return (otyp == WHITE_DRAGON_SCALES); /* white but not silver */
640 case AD_DRST: /* drain strength => poison */
641 case AD_DISE: /* blocks disease but not slime */
642 return (otyp == GREEN_DRAGON_SCALES);
643 case AD_SLEE: /* sleep */
644 case AD_PLYS: /* paralysis => free action */
645 return (otyp == ORANGE_DRAGON_SCALES);
646 case AD_DISN: /* disintegration */
647 case AD_DRLI: /* level drain resistance */
648 return (otyp == BLACK_DRAGON_SCALES);
649 case AD_ELEC: /* electricity == lightning */
650 case AD_SLOW: /* confers speed so blocks speed removal */
651 return (otyp == BLUE_DRAGON_SCALES);
652 case AD_ACID:
653 case AD_STON: /* petrification resistance */
654 return (otyp == YELLOW_DRAGON_SCALES);
655 default:
656 /* SILVER_DRAGON_SCALES don't resist any particular attack type */
657 break;
660 return FALSE;
663 /* used for monsters */
664 boolean
665 defends_when_carried(int adtyp, struct obj *otmp)
667 const struct artifact *weap;
669 if ((weap = get_artifact(otmp)) != &artilist[ART_NONARTIFACT])
670 return (boolean) (weap->cary.adtyp == adtyp);
671 return FALSE;
674 /* determine whether an item confers Protection */
675 boolean
676 protects(struct obj *otmp, boolean being_worn)
678 const struct artifact *arti;
680 if (being_worn && objects[otmp->otyp].oc_oprop == PROTECTION)
681 return TRUE;
682 arti = get_artifact(otmp);
683 if (arti == &artilist[ART_NONARTIFACT])
684 return FALSE;
685 return (boolean) ((arti->cspfx & SPFX_PROTECT) != 0
686 || (being_worn && (arti->spfx & SPFX_PROTECT) != 0));
690 * a potential artifact has just been worn/wielded/picked-up or
691 * unworn/unwielded/dropped. Pickup/drop only set/reset the W_ART mask.
693 void
694 set_artifact_intrinsic(
695 struct obj *otmp,
696 boolean on,
697 long wp_mask)
699 long *mask = 0;
700 const struct artifact *art, *oart = get_artifact(otmp);
701 struct obj *obj;
702 uchar dtyp;
703 long spfx;
705 if (oart == &artilist[ART_NONARTIFACT])
706 return;
708 /* effects from the defn field */
709 dtyp = (wp_mask != W_ART) ? oart->defn.adtyp : oart->cary.adtyp;
711 if (dtyp == AD_FIRE)
712 mask = &EFire_resistance;
713 else if (dtyp == AD_COLD)
714 mask = &ECold_resistance;
715 else if (dtyp == AD_ELEC)
716 mask = &EShock_resistance;
717 else if (dtyp == AD_MAGM)
718 mask = &EAntimagic;
719 else if (dtyp == AD_DISN)
720 mask = &EDisint_resistance;
721 else if (dtyp == AD_DRST)
722 mask = &EPoison_resistance;
723 else if (dtyp == AD_DRLI)
724 mask = &EDrain_resistance;
726 if (mask && wp_mask == W_ART && !on) {
727 /* find out if some other artifact also confers this intrinsic;
728 if so, leave the mask alone */
729 for (obj = gi.invent; obj; obj = obj->nobj) {
730 if (obj != otmp && obj->oartifact) {
731 art = get_artifact(obj);
732 if (art != &artilist[ART_NONARTIFACT]
733 && art->cary.adtyp == dtyp) {
734 mask = (long *) 0;
735 break;
740 if (mask) {
741 if (on)
742 *mask |= wp_mask;
743 else
744 *mask &= ~wp_mask;
747 /* intrinsics from the spfx field; there could be more than one */
748 spfx = (wp_mask != W_ART) ? oart->spfx : oart->cspfx;
749 if (spfx && wp_mask == W_ART && !on) {
750 /* don't change any spfx also conferred by other artifacts */
751 for (obj = gi.invent; obj; obj = obj->nobj)
752 if (obj != otmp && obj->oartifact) {
753 art = get_artifact(obj);
754 if (art != &artilist[ART_NONARTIFACT])
755 spfx &= ~art->cspfx;
759 if (spfx & SPFX_SEARCH) {
760 if (on)
761 ESearching |= wp_mask;
762 else
763 ESearching &= ~wp_mask;
765 if (spfx & SPFX_HALRES) {
766 /* make_hallucinated must (re)set the mask itself to get
767 * the display right */
768 /* restoring needed because this is the only artifact intrinsic
769 * that can print a message--need to guard against being printed
770 * when restoring a game
772 (void) make_hallucinated((long) !on,
773 program_state.restoring ? FALSE : TRUE,
774 wp_mask);
776 if (spfx & SPFX_ESP) {
777 if (on)
778 ETelepat |= wp_mask;
779 else
780 ETelepat &= ~wp_mask;
781 recalc_telepat_range();
782 see_monsters();
784 if (spfx & SPFX_STLTH) {
785 if (on)
786 EStealth |= wp_mask;
787 else
788 EStealth &= ~wp_mask;
790 if (spfx & SPFX_REGEN) {
791 if (on)
792 ERegeneration |= wp_mask;
793 else
794 ERegeneration &= ~wp_mask;
796 if (spfx & SPFX_TCTRL) {
797 if (on)
798 ETeleport_control |= wp_mask;
799 else
800 ETeleport_control &= ~wp_mask;
802 if (spfx & SPFX_WARN) {
803 if (spec_m2(otmp)) {
804 if (on) {
805 EWarn_of_mon |= wp_mask;
806 svc.context.warntype.obj |= spec_m2(otmp);
807 } else {
808 EWarn_of_mon &= ~wp_mask;
809 svc.context.warntype.obj &= ~spec_m2(otmp);
811 see_monsters();
812 } else {
813 if (on)
814 EWarning |= wp_mask;
815 else
816 EWarning &= ~wp_mask;
819 if (spfx & SPFX_EREGEN) {
820 if (on)
821 EEnergy_regeneration |= wp_mask;
822 else
823 EEnergy_regeneration &= ~wp_mask;
825 if (spfx & SPFX_HSPDAM) {
826 if (on)
827 EHalf_spell_damage |= wp_mask;
828 else
829 EHalf_spell_damage &= ~wp_mask;
831 if (spfx & SPFX_HPHDAM) {
832 if (on)
833 EHalf_physical_damage |= wp_mask;
834 else
835 EHalf_physical_damage &= ~wp_mask;
837 if (spfx & SPFX_XRAY) {
838 /* this assumes that no one else is using xray_range */
839 if (on)
840 u.xray_range = 3;
841 else
842 u.xray_range = -1;
843 gv.vision_full_recalc = 1;
845 if ((spfx & SPFX_REFLECT) && (wp_mask & W_WEP)) {
846 if (on)
847 EReflecting |= wp_mask;
848 else
849 EReflecting &= ~wp_mask;
851 if (spfx & SPFX_PROTECT) {
852 if (on)
853 EProtection |= wp_mask;
854 else
855 EProtection &= ~wp_mask;
858 if (wp_mask == W_ART && !on && oart->inv_prop) {
859 /* might have to turn off invoked power too */
860 if (oart->inv_prop <= LAST_PROP
861 && (u.uprops[oart->inv_prop].extrinsic & W_ARTI))
862 (void) arti_invoke(otmp);
865 if (wp_mask == W_WEP && is_art(otmp, ART_SUNSWORD)) {
866 if (on)
867 EBlnd_resist |= wp_mask;
868 else
869 EBlnd_resist &= ~wp_mask;
873 /* touch_artifact()'s return value isn't sufficient to tell whether it
874 dished out damage, and tracking changes to u.uhp, u.mh, Lifesaved
875 when trying to avoid second wounding is too cumbersome */
876 static boolean touch_blasted; /* for retouch_object() */
879 * creature (usually hero) tries to touch (pick up or wield) an artifact obj.
880 * Returns 0 if the object refuses to be touched.
881 * This routine does not change any object chains.
882 * Ignores such things as gauntlets, assuming the artifact is not
883 * fooled by such trappings.
886 touch_artifact(struct obj *obj, struct monst *mon)
888 const struct artifact *oart = get_artifact(obj);
889 boolean badclass, badalign, self_willed, yours;
891 touch_blasted = FALSE;
892 if (oart == &artilist[ART_NONARTIFACT])
893 return 1;
895 yours = (mon == &gy.youmonst);
896 /* all quest artifacts are self-willed; if this ever changes, `badclass'
897 will have to be extended to explicitly include quest artifacts */
898 self_willed = ((oart->spfx & SPFX_INTEL) != 0);
899 if (yours) {
900 badclass = self_willed
901 && ((oart->role != NON_PM && !Role_if(oart->role))
902 || (oart->race != NON_PM && !Race_if(oart->race)));
903 badalign = ((oart->spfx & SPFX_RESTR) != 0
904 && oart->alignment != A_NONE
905 && (oart->alignment != u.ualign.type
906 || u.ualign.record < 0));
907 } else if (!is_covetous(mon->data) && !is_mplayer(mon->data)) {
908 badclass = self_willed && oart->role != NON_PM
909 && oart != &artilist[ART_EXCALIBUR];
910 badalign = (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE
911 && (oart->alignment != mon_aligntyp(mon));
912 } else { /* an M3_WANTSxxx monster or a fake player */
913 /* special monsters trying to take the Amulet, invocation tools or
914 quest item can touch anything except `spec_applies' artifacts */
915 badclass = badalign = FALSE;
917 /* weapons which attack specific categories of monsters are
918 bad for them even if their alignments happen to match */
919 if (!badalign)
920 badalign = bane_applies(oart, mon);
922 if (((badclass || badalign) && self_willed)
923 || (badalign && (!yours || !rn2(4)))) {
924 int dmg, tmp;
925 char buf[BUFSZ];
927 if (!yours)
928 return 0;
929 You("are blasted by %s power!", s_suffix(the(xname(obj))));
930 touch_blasted = TRUE;
931 dmg = d((Antimagic ? 2 : 4), (self_willed ? 10 : 4));
932 /* add half (maybe quarter) of the usual silver damage bonus */
933 if (objects[obj->otyp].oc_material == SILVER && Hate_silver)
934 tmp = rnd(10), dmg += Maybe_Half_Phys(tmp);
935 Sprintf(buf, "touching %s", oart->name);
936 losehp(dmg, buf, KILLED_BY); /* magic damage, not physical */
937 exercise(A_WIS, FALSE);
940 /* can pick it up unless you're totally non-synch'd with the artifact */
941 if (badclass && badalign && self_willed) {
942 if (yours) {
943 if (!carried(obj))
944 pline("%s your grasp!", Tobjnam(obj, "evade"));
945 else
946 pline("%s beyond your control!", Tobjnam(obj, "are"));
948 return 0;
951 return 1;
954 /* decide whether an artifact itself is vulnerable to a particular type
955 of erosion damage, independent of the properties of its bearer */
956 boolean
957 arti_immune(struct obj *obj, int dtyp)
959 const struct artifact *weap = get_artifact(obj);
961 if (weap == &artilist[ART_NONARTIFACT])
962 return FALSE;
963 if (dtyp == AD_PHYS)
964 return FALSE; /* nothing is immune to phys dmg */
965 return (boolean) (weap->attk.adtyp == dtyp
966 || weap->defn.adtyp == dtyp
967 || weap->cary.adtyp == dtyp);
970 staticfn boolean
971 bane_applies(const struct artifact *oart, struct monst *mon)
973 struct artifact atmp;
975 if (oart != &artilist[ART_NONARTIFACT]
976 && (oart->spfx & SPFX_DBONUS) != 0) {
977 atmp = *oart;
978 atmp.spfx &= SPFX_DBONUS; /* clear other spfx fields */
979 if (spec_applies(&atmp, mon))
980 return TRUE;
982 return FALSE;
985 /* decide whether an artifact's special attacks apply against mtmp */
986 staticfn int
987 spec_applies(const struct artifact *weap, struct monst *mtmp)
989 struct permonst *ptr;
990 boolean yours;
992 if (!(weap->spfx & (SPFX_DBONUS | SPFX_ATTK)))
993 return (weap->attk.adtyp == AD_PHYS);
995 yours = (mtmp == &gy.youmonst);
996 ptr = mtmp->data;
998 if (weap->spfx & SPFX_DMONS) {
999 return (ptr == &mons[(int) weap->mtype]);
1000 } else if (weap->spfx & SPFX_DCLAS) {
1001 return (weap->mtype == (unsigned long) ptr->mlet);
1002 } else if (weap->spfx & SPFX_DFLAG1) {
1003 return ((ptr->mflags1 & weap->mtype) != 0L);
1004 } else if (weap->spfx & SPFX_DFLAG2) {
1005 return ((ptr->mflags2 & weap->mtype)
1006 || (yours
1007 && ((!Upolyd && (gu.urace.selfmask & weap->mtype))
1008 || ((weap->mtype & M2_WERE) && ismnum(u.ulycn)))));
1009 } else if (weap->spfx & SPFX_DALIGN) {
1010 return yours ? (u.ualign.type != weap->alignment)
1011 : (ptr->maligntyp == A_NONE
1012 || sgn(ptr->maligntyp) != weap->alignment);
1013 } else if (weap->spfx & SPFX_ATTK) {
1014 if (defended(mtmp, (int) weap->attk.adtyp))
1015 return FALSE;
1017 switch (weap->attk.adtyp) {
1018 case AD_FIRE:
1019 return !(yours ? Fire_resistance : resists_fire(mtmp));
1020 case AD_COLD:
1021 return !(yours ? Cold_resistance : resists_cold(mtmp));
1022 case AD_ELEC:
1023 return !(yours ? Shock_resistance : resists_elec(mtmp));
1024 case AD_MAGM:
1025 case AD_STUN:
1026 return !(yours ? Antimagic : (rn2(100) < ptr->mr));
1027 case AD_DRST:
1028 return !(yours ? Poison_resistance : resists_poison(mtmp));
1029 case AD_DRLI:
1030 return !(yours ? Drain_resistance : resists_drli(mtmp));
1031 case AD_STON:
1032 return !(yours ? Stone_resistance : resists_ston(mtmp));
1033 default:
1034 impossible("Weird weapon special attack.");
1037 return 0;
1040 /* return the M2 flags of monster that an artifact's special attacks apply
1041 * against */
1042 long
1043 spec_m2(struct obj *otmp)
1045 const struct artifact *artifact = get_artifact(otmp);
1047 if (artifact != &artilist[ART_NONARTIFACT])
1048 return artifact->mtype;
1049 return 0L;
1052 /* special attack bonus */
1054 spec_abon(struct obj *otmp, struct monst *mon)
1056 const struct artifact *weap = get_artifact(otmp);
1058 /* no need for an extra check for `NO_ATTK' because this will
1059 always return 0 for any artifact which has that attribute */
1061 if (weap != &artilist[ART_NONARTIFACT]
1062 && weap->attk.damn && spec_applies(weap, mon))
1063 return rnd((int) weap->attk.damn);
1064 return 0;
1067 /* special damage bonus */
1069 spec_dbon(struct obj *otmp, struct monst *mon, int tmp)
1071 const struct artifact *weap = get_artifact(otmp);
1073 if ((weap == &artilist[ART_NONARTIFACT])
1074 || (weap->attk.adtyp == AD_PHYS /* check for `NO_ATTK' */
1075 && weap->attk.damn == 0 && weap->attk.damd == 0))
1076 gs.spec_dbon_applies = FALSE;
1077 else if (is_art(otmp, ART_GRIMTOOTH))
1078 /* Grimtooth has SPFX settings to warn against elves but we want its
1079 damage bonus to apply to all targets, so bypass spec_applies() */
1080 gs.spec_dbon_applies = TRUE;
1081 else
1082 gs.spec_dbon_applies = spec_applies(weap, mon);
1084 if (gs.spec_dbon_applies)
1085 return weap->attk.damd ? rnd((int) weap->attk.damd) : max(tmp, 1);
1086 return 0;
1089 /* add identified artifact to discoveries list */
1090 void
1091 discover_artifact(xint16 m)
1093 int i;
1095 /* look for this artifact in the discoveries list;
1096 if we hit an empty slot then it's not present, so add it */
1097 for (i = 0; i < NROFARTIFACTS; i++)
1098 if (artidisco[i] == 0 || artidisco[i] == m) {
1099 artidisco[i] = m;
1100 return;
1102 /* there is one slot per artifact, so we should never reach the
1103 end without either finding the artifact or an empty slot... */
1104 impossible("couldn't discover artifact (%d)", (int) m);
1107 /* used to decide whether an artifact has been fully identified */
1108 boolean
1109 undiscovered_artifact(xint16 m)
1111 int i;
1113 /* look for this artifact in the discoveries list;
1114 if we hit an empty slot then it's undiscovered */
1115 for (i = 0; i < NROFARTIFACTS; i++)
1116 if (artidisco[i] == m)
1117 return FALSE;
1118 else if (artidisco[i] == 0)
1119 break;
1120 return TRUE;
1123 /* display a list of discovered artifacts; return their count */
1125 disp_artifact_discoveries(
1126 winid tmpwin) /* supplied by dodiscover(); type is NHW_TEXT */
1128 int i, m, otyp;
1129 const char *algnstr;
1130 char buf[BUFSZ];
1132 for (i = 0; i < NROFARTIFACTS; i++) {
1133 if (artidisco[i] == 0)
1134 break; /* empty slot implies end of list */
1135 if (tmpwin == WIN_ERR)
1136 continue; /* for WIN_ERR, we just count */
1138 if (i == 0)
1139 putstr(tmpwin, iflags.menu_headings.attr, "Artifacts");
1140 m = artidisco[i];
1141 otyp = artilist[m].otyp;
1142 algnstr = align_str(artilist[m].alignment);
1143 if (!strcmp(algnstr, "unaligned"))
1144 algnstr = "non-aligned";
1146 Sprintf(buf, " %s [%s %s]", artiname(m),
1147 algnstr, simple_typename(otyp));
1148 putstr(tmpwin, 0, buf);
1150 return i;
1153 /* (wizard mode only) show all artifacts and their flags */
1154 void
1155 dump_artifact_info(winid tmpwin)
1157 int m;
1158 char buf[BUFSZ], buf2[BUFSZ];
1160 /* not a menu, but header uses same bold or whatever attribute as such */
1161 putstr(tmpwin, iflags.menu_headings.attr, "Artifacts");
1162 for (m = 1; m <= NROFARTIFACTS; ++m) {
1163 Snprintf(buf2, sizeof buf2,
1164 "[%s%s%s%s%s%s%s%s%s]", /* 9 bits overall */
1165 artiexist[m].exists ? "exists;" : "",
1166 artiexist[m].found ? " hero knows;" : "",
1167 /* .exists and .found have different punctuation because
1168 they're expected to be combined with one of these */
1169 artiexist[m].gift ? " gift" : "",
1170 artiexist[m].wish ? " wish" : "",
1171 artiexist[m].named ? " named" : "",
1172 artiexist[m].viadip ? " viadip" : "",
1173 artiexist[m].lvldef ? " lvldef" : "",
1174 artiexist[m].bones ? " bones" : "",
1175 artiexist[m].rndm ? " random" : "");
1176 #if 0 /* 'tmpwin' here is a text window, not a menu */
1177 if (iflags.menu_tab_sep)
1178 Sprintf(buf, " %s\t%s", artiname(m), buf2);
1179 else
1180 #else
1181 /* "The Platinum Yendorian Express Card" is 35 characters */
1182 Snprintf(buf, sizeof buf, " %-36.36s%s", artiname(m), buf2);
1183 #endif
1184 putstr(tmpwin, 0, buf);
1186 return;
1190 * Magicbane's intrinsic magic is incompatible with normal
1191 * enchantment magic. Thus, its effects have a negative
1192 * dependence on spe. Against low mr victims, it typically
1193 * does "double athame" damage, 2d4. Occasionally, it will
1194 * cast unbalancing magic which effectively averages out to
1195 * 4d4 damage (3d4 against high mr victims), for spe = 0.
1197 * Prior to 3.4.1, the cancel (aka purge) effect always
1198 * included the scare effect too; now it's one or the other.
1199 * Likewise, the stun effect won't be combined with either
1200 * of those two; it will be chosen separately or possibly
1201 * used as a fallback when scare or cancel fails.
1203 * [Historical note: a change to artifact_hit() for 3.4.0
1204 * unintentionally made all of Magicbane's special effects
1205 * be blocked if the defender successfully saved against a
1206 * stun attack. As of 3.4.1, those effects can occur but
1207 * will be slightly less likely than they were in 3.3.x.]
1210 enum mb_effect_indices {
1211 MB_INDEX_PROBE = 0,
1212 MB_INDEX_STUN,
1213 MB_INDEX_SCARE,
1214 MB_INDEX_CANCEL,
1216 NUM_MB_INDICES
1219 #define MB_MAX_DIEROLL 8 /* rolls above this aren't magical */
1220 static const char *const mb_verb[2][NUM_MB_INDICES] = {
1221 { "probe", "stun", "scare", "cancel" },
1222 { "prod", "amaze", "tickle", "purge" },
1225 /* called when someone is being hit by Magicbane */
1226 staticfn boolean
1227 Mb_hit(struct monst *magr, /* attacker */
1228 struct monst *mdef, /* defender */
1229 struct obj *mb, /* Magicbane */
1230 int *dmgptr, /* extra damage target will suffer */
1231 int dieroll, /* d20 that has already scored a hit */
1232 boolean vis, /* whether the action can be seen */
1233 char *hittee) /* target's name: "you" or mon_nam(mdef) */
1235 struct permonst *old_mdat;
1236 const char *verb;
1237 boolean youattack = (magr == &gy.youmonst),
1238 youdefend = (mdef == &gy.youmonst),
1239 resisted = FALSE, do_stun, do_confuse, result;
1240 int attack_indx, fakeidx, scare_dieroll = MB_MAX_DIEROLL / 2;
1242 result = FALSE; /* no message given yet */
1243 /* the most severe effects are less likely at higher enchantment */
1244 if (mb->spe >= 3)
1245 scare_dieroll /= (1 << (mb->spe / 3));
1246 /* if target successfully resisted the artifact damage bonus,
1247 reduce overall likelihood of the assorted special effects */
1248 if (!gs.spec_dbon_applies)
1249 dieroll += 1;
1251 /* might stun even when attempting a more severe effect, but
1252 in that case it will only happen if the other effect fails;
1253 extra damage will apply regardless; 3.4.1: sometimes might
1254 just probe even when it hasn't been enchanted */
1255 do_stun = (max(mb->spe, 0) < rn2(gs.spec_dbon_applies ? 11 : 7));
1257 /* the special effects also boost physical damage; increments are
1258 generally cumulative, but since the stun effect is based on a
1259 different criterium its damage might not be included; the base
1260 damage is either 1d4 (athame) or 2d4 (athame+spec_dbon) depending
1261 on target's resistance check against AD_STUN (handled by caller)
1262 [note that a successful save against AD_STUN doesn't actually
1263 prevent the target from ending up stunned] */
1264 attack_indx = MB_INDEX_PROBE;
1265 *dmgptr += rnd(4); /* (2..3)d4 */
1266 if (do_stun) {
1267 attack_indx = MB_INDEX_STUN;
1268 *dmgptr += rnd(4); /* (3..4)d4 */
1270 if (dieroll <= scare_dieroll) {
1271 attack_indx = MB_INDEX_SCARE;
1272 *dmgptr += rnd(4); /* (3..5)d4 */
1274 if (dieroll <= (scare_dieroll / 2)) {
1275 attack_indx = MB_INDEX_CANCEL;
1276 *dmgptr += rnd(4); /* (4..6)d4 */
1279 /* give the hit message prior to inflicting the effects */
1280 verb = mb_verb[!!Hallucination][attack_indx];
1281 if (youattack || youdefend || vis) {
1282 result = TRUE;
1283 pline_The("magic-absorbing blade %s %s!",
1284 vtense((const char *) 0, verb), hittee);
1285 /* assume probing has some sort of noticeable feedback
1286 even if it is being done by one monster to another */
1287 if (attack_indx == MB_INDEX_PROBE && !canspotmon(mdef))
1288 map_invisible(mdef->mx, mdef->my);
1291 /* now perform special effects */
1292 switch (attack_indx) {
1293 case MB_INDEX_CANCEL:
1294 old_mdat = youdefend ? gy.youmonst.data : mdef->data;
1295 /* No mdef->mcan check: even a cancelled monster can be polymorphed
1296 * into a golem, and the "cancel" effect acts as if some magical
1297 * energy remains in spellcasting defenders to be absorbed later.
1299 if (!cancel_monst(mdef, mb, youattack, FALSE, FALSE)) {
1300 resisted = TRUE;
1301 } else {
1302 do_stun = FALSE;
1303 if (youdefend) {
1304 if (gy.youmonst.data != old_mdat)
1305 *dmgptr = 0; /* rehumanized, so no more damage */
1306 if (u.uenmax > 0) {
1307 u.uenmax--;
1308 if (u.uen > 0)
1309 u.uen--;
1310 disp.botl = TRUE;
1311 You("lose magical energy!");
1313 } else {
1314 /* canceled shapeshifter/vamp may have changed forms, so
1315 update its name if necessary */
1316 if (mdef->data != old_mdat)
1317 Strcpy(hittee, mon_nam(mdef));
1318 if (mdef->data == &mons[PM_CLAY_GOLEM])
1319 mdef->mhp = 1; /* cancelled clay golems will die */
1320 if (youattack && attacktype(mdef->data, AT_MAGC)) {
1321 u.uenmax++;
1322 if (u.uenmax > u.uenpeak)
1323 u.uenpeak = u.uenmax;
1324 u.uen++;
1325 disp.botl = TRUE;
1326 You("absorb magical energy!");
1330 break;
1332 case MB_INDEX_SCARE:
1333 if (youdefend) {
1334 if (Antimagic) {
1335 resisted = TRUE;
1336 } else {
1337 nomul(-3);
1338 gm.multi_reason = "being scared stiff";
1339 gn.nomovemsg = "";
1340 if (magr && magr == u.ustuck && sticks(gy.youmonst.data)) {
1341 set_ustuck((struct monst *) 0);
1342 You("release %s!", mon_nam(magr));
1345 } else {
1346 if (rn2(2) && resist(mdef, WEAPON_CLASS, 0, NOTELL))
1347 resisted = TRUE;
1348 else
1349 monflee(mdef, 3, FALSE, (mdef->mhp > *dmgptr));
1351 if (!resisted)
1352 do_stun = FALSE;
1353 break;
1355 case MB_INDEX_STUN:
1356 do_stun = TRUE; /* (this is redundant...) */
1357 break;
1359 case MB_INDEX_PROBE:
1360 if (youattack && (mb->spe == 0 || !rn2(3 * abs(mb->spe)))) {
1361 pline_The("%s is insightful.", verb);
1362 /* pre-damage status */
1363 probe_monster(mdef);
1365 break;
1367 /* stun if that was selected and a worse effect didn't occur */
1368 if (do_stun) {
1369 if (youdefend)
1370 make_stunned(((HStun & TIMEOUT) + 3L), FALSE);
1371 else
1372 mdef->mstun = 1;
1373 /* avoid extra stun message below if we used mb_verb["stun"] above */
1374 if (attack_indx == MB_INDEX_STUN)
1375 do_stun = FALSE;
1377 /* lastly, all this magic can be confusing... */
1378 do_confuse = !rn2(12);
1379 if (do_confuse) {
1380 if (youdefend)
1381 make_confused((HConfusion & TIMEOUT) + 4L, FALSE);
1382 else
1383 mdef->mconf = 1;
1386 /* now give message(s) describing side-effects; Use fakename
1387 so vtense() won't be fooled by assigned name ending in 's' */
1388 fakeidx = youdefend ? 1 : 0;
1389 if (youattack || youdefend || vis) {
1390 (void) upstart(hittee); /* capitalize */
1391 if (resisted) {
1392 pline("%s %s!", hittee, vtense(fakename[fakeidx], "resist"));
1393 shieldeff(youdefend ? u.ux : mdef->mx,
1394 youdefend ? u.uy : mdef->my);
1396 if ((do_stun || do_confuse) && flags.verbose) {
1397 char buf[BUFSZ];
1399 buf[0] = '\0';
1400 if (do_stun)
1401 Strcat(buf, "stunned");
1402 if (do_stun && do_confuse)
1403 Strcat(buf, " and ");
1404 if (do_confuse)
1405 Strcat(buf, "confused");
1406 pline("%s %s %s%c", hittee, vtense(fakename[fakeidx], "are"), buf,
1407 (do_stun && do_confuse) ? '!' : '.');
1411 return result;
1414 DISABLE_WARNING_FORMAT_NONLITERAL
1416 /* Function used when someone attacks someone else with an artifact
1417 * weapon. Only adds the special (artifact) damage, and returns a 1 if it
1418 * did something special (in which case the caller won't print the normal
1419 * hit message). This should be called once upon every artifact attack;
1420 * dmgval() no longer takes artifact bonuses into account. Possible
1421 * extension: change the killer so that when an orc kills you with
1422 * Stormbringer it's "killed by Stormbringer" instead of "killed by an orc".
1424 boolean
1425 artifact_hit(
1426 struct monst *magr, /* attacker; might be Null if 'mdef' is youmonst */
1427 struct monst *mdef, /* defender */
1428 struct obj *otmp, /* artifact weapon */
1429 int *dmgptr, /* output */
1430 int dieroll) /* needed for Magicbane and vorpal blades */
1432 boolean youattack = (magr == &gy.youmonst);
1433 boolean youdefend = (mdef == &gy.youmonst);
1434 boolean vis = (!youattack && magr && cansee(magr->mx, magr->my))
1435 || (!youdefend && cansee(mdef->mx, mdef->my))
1436 || (youattack && engulfing_u(mdef) && !Blind);
1437 boolean realizes_damage;
1438 const char *wepdesc;
1439 static const char you[] = "you";
1440 char hittee[BUFSZ];
1442 Strcpy(hittee, youdefend ? you : mon_nam(mdef));
1444 /* The following takes care of most of the damage, but not all--
1445 * the exception being for level draining, which is specially
1446 * handled. Messages are done in this function, however.
1448 *dmgptr += spec_dbon(otmp, mdef, *dmgptr);
1450 if (youattack && youdefend) {
1451 impossible("attacking yourself with weapon?");
1452 return FALSE;
1455 realizes_damage = (youdefend || vis
1456 /* feel the effect even if not seen */
1457 || (youattack && mdef == u.ustuck));
1459 /* the four basic attacks: fire, cold, shock and missiles */
1460 if (attacks(AD_FIRE, otmp)) {
1461 if (realizes_damage)
1462 pline_The("fiery blade %s %s%c",
1463 !gs.spec_dbon_applies
1464 ? "hits"
1465 : (mdef->data == &mons[PM_WATER_ELEMENTAL])
1466 ? "vaporizes part of"
1467 : "burns",
1468 hittee, !gs.spec_dbon_applies ? '.' : '!');
1469 if (!rn2(4)) {
1470 int itemdmg = destroy_items(mdef, AD_FIRE, *dmgptr);
1471 if (!youdefend)
1472 *dmgptr += itemdmg; /* item destruction dmg */
1473 ignite_items(mdef->minvent);
1475 if (youdefend && Slimed)
1476 burn_away_slime();
1477 return realizes_damage;
1479 if (attacks(AD_COLD, otmp)) {
1480 if (realizes_damage)
1481 pline_The("ice-cold blade %s %s%c",
1482 !gs.spec_dbon_applies ? "hits" : "freezes", hittee,
1483 !gs.spec_dbon_applies ? '.' : '!');
1484 if (!rn2(4)) {
1485 int itemdmg = destroy_items(mdef, AD_COLD, *dmgptr);
1486 if (!youdefend)
1487 *dmgptr += itemdmg; /* item destruction dmg */
1489 return realizes_damage;
1491 if (attacks(AD_ELEC, otmp)) {
1492 if (realizes_damage)
1493 pline_The("massive hammer hits%s %s%c",
1494 !gs.spec_dbon_applies ? "" : "! Lightning strikes",
1495 hittee, !gs.spec_dbon_applies ? '.' : '!');
1496 if (gs.spec_dbon_applies)
1497 wake_nearto(mdef->mx, mdef->my, 4 * 4);
1498 if (!rn2(5)) {
1499 int itemdmg = destroy_items(mdef, AD_ELEC, *dmgptr);
1500 if (!youdefend)
1501 *dmgptr += itemdmg; /* item destruction dmg */
1503 return realizes_damage;
1505 if (attacks(AD_MAGM, otmp)) {
1506 if (realizes_damage)
1507 pline_The("imaginary widget hits%s %s%c",
1508 !gs.spec_dbon_applies
1509 ? ""
1510 : "! A hail of magic missiles strikes",
1511 hittee, !gs.spec_dbon_applies ? '.' : '!');
1512 return realizes_damage;
1515 if (attacks(AD_STUN, otmp) && dieroll <= MB_MAX_DIEROLL) {
1516 /* Magicbane's special attacks (possibly modifies hittee[]) */
1517 return Mb_hit(magr, mdef, otmp, dmgptr, dieroll, vis, hittee);
1520 if (!gs.spec_dbon_applies) {
1521 /* since damage bonus didn't apply, nothing more to do;
1522 no further attacks have side-effects on inventory */
1523 return FALSE;
1526 /* We really want "on a natural 20" but Nethack does it in */
1527 /* reverse from AD&D. */
1528 if (spec_ability(otmp, SPFX_BEHEAD)) {
1529 if (is_art(otmp, ART_TSURUGI_OF_MURAMASA) && dieroll == 1) {
1530 wepdesc = "The razor-sharp blade";
1531 /* not really beheading, but so close, why add another SPFX */
1532 if (youattack && engulfing_u(mdef)) {
1533 You("slice %s wide open!", mon_nam(mdef));
1534 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1535 return TRUE;
1537 if (!youdefend) {
1538 /* allow normal cutworm() call to add extra damage */
1539 if (gn.notonhead)
1540 return FALSE;
1542 if (bigmonst(mdef->data)) {
1543 if (youattack)
1544 You("slice deeply into %s!", mon_nam(mdef));
1545 else if (vis)
1546 pline("%s cuts deeply into %s!", Monnam(magr),
1547 hittee);
1548 *dmgptr *= 2;
1549 return TRUE;
1551 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1552 pline("%s cuts %s in half!", wepdesc, mon_nam(mdef));
1553 otmp->dknown = TRUE;
1554 return TRUE;
1555 } else {
1556 if (bigmonst(gy.youmonst.data)) {
1557 pline("%s cuts deeply into you!",
1558 magr ? Monnam(magr) : wepdesc);
1559 *dmgptr *= 2;
1560 return TRUE;
1563 /* Players with negative AC's take less damage instead
1564 * of just not getting hit. We must add a large enough
1565 * value to the damage so that this reduction in
1566 * damage does not prevent death.
1568 *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
1569 pline("%s cuts you in half!", wepdesc);
1570 otmp->dknown = TRUE;
1571 return TRUE;
1573 } else if (is_art(otmp, ART_VORPAL_BLADE)
1574 && (dieroll == 1 || mdef->data == &mons[PM_JABBERWOCK])) {
1575 static const char *const behead_msg[2] = { "%s beheads %s!",
1576 "%s decapitates %s!" };
1578 if (youattack && engulfing_u(mdef))
1579 return FALSE;
1580 wepdesc = artilist[ART_VORPAL_BLADE].name;
1581 if (!youdefend) {
1582 if (!has_head(mdef->data) || gn.notonhead || u.uswallow) {
1583 if (youattack)
1584 pline("Somehow, you miss %s wildly.", mon_nam(mdef));
1585 else if (vis)
1586 pline("Somehow, %s misses wildly.", mon_nam(magr));
1587 *dmgptr = 0;
1588 return (boolean) (youattack || vis);
1590 if (noncorporeal(mdef->data) || amorphous(mdef->data)) {
1591 pline("%s slices through %s %s.", wepdesc,
1592 s_suffix(mon_nam(mdef)), mbodypart(mdef, NECK));
1593 return TRUE;
1595 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1596 pline(ROLL_FROM(behead_msg), wepdesc,
1597 mon_nam(mdef));
1598 if (Hallucination && !flags.female)
1599 pline("Good job Henry, but that wasn't Anne.");
1600 otmp->dknown = TRUE;
1601 return TRUE;
1602 } else {
1603 if (!has_head(gy.youmonst.data)) {
1604 pline("Somehow, %s misses you wildly.",
1605 magr ? mon_nam(magr) : wepdesc);
1606 *dmgptr = 0;
1607 return TRUE;
1609 if (noncorporeal(gy.youmonst.data)
1610 || amorphous(gy.youmonst.data)) {
1611 pline("%s slices through your %s.", wepdesc,
1612 body_part(NECK));
1613 return TRUE;
1615 *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
1616 pline(ROLL_FROM(behead_msg), wepdesc, "you");
1617 otmp->dknown = TRUE;
1618 /* Should amulets fall off? */
1619 return TRUE;
1623 if (spec_ability(otmp, SPFX_DRLI)) {
1624 /* some non-living creatures (golems, vortices) are vulnerable to
1625 life drain effects so can get "<Arti> draws the <life>" feedback */
1626 const char *life = nonliving(mdef->data) ? "animating force" : "life";
1628 if (!youdefend) {
1629 int m_lev = (int) mdef->m_lev, /* will be 0 for 1d4 mon */
1630 mhpmax = mdef->mhpmax,
1631 drain = monhp_per_lvl(mdef); /* usually 1d8 */
1632 /* note: DRLI attack uses 2d6, attacker doesn't get healed */
1634 /* stop draining HP if it drops too low (still drains level;
1635 also caller still inflicts regular weapon damage) */
1636 if (mhpmax - drain <= m_lev)
1637 drain = (mhpmax > m_lev) ? (mhpmax - (m_lev + 1)) : 0;
1639 if (vis) {
1640 /* call distant_name() for possible side-effects even if
1641 the result won't be printed */
1642 char *otmpname = distant_name(otmp, xname);
1644 if (is_art(otmp, ART_STORMBRINGER))
1645 pline_The("%s blade draws the %s from %s!",
1646 hcolor(NH_BLACK), life, mon_nam(mdef));
1647 else
1648 pline("%s draws the %s from %s!",
1649 The(otmpname), life, mon_nam(mdef));
1651 if (mdef->m_lev == 0) {
1652 /* losing a level when at 0 is fatal */
1653 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1654 } else {
1655 *dmgptr += drain;
1656 mdef->mhpmax -= drain;
1657 mdef->m_lev--;
1660 if (drain > 0) {
1661 /* drain: was target's damage, now heal attacker by half */
1662 drain = (drain + 1) / 2; /* drain/2 rounded up */
1663 if (youattack) {
1664 healup(drain, 0, FALSE, FALSE);
1665 } else {
1666 assert(magr != 0);
1667 healmon(magr, drain, 0);
1670 return vis;
1671 } else { /* youdefend */
1672 int oldhpmax = u.uhpmax;
1674 if (Blind) {
1675 You_feel("an %s drain your %s!",
1676 is_art(otmp, ART_STORMBRINGER)
1677 ? "unholy blade"
1678 : "object",
1679 life);
1680 } else {
1681 /* call distant_name() for possible side-effects even if
1682 the result won't be printed */
1683 char *otmpname = distant_name(otmp, xname);
1685 if (is_art(otmp, ART_STORMBRINGER))
1686 pline_The("%s blade drains your %s!",
1687 hcolor(NH_BLACK), life);
1688 else
1689 pline("%s drains your %s!", The(otmpname), life);
1691 losexp("life drainage");
1692 if (magr && magr->mhp < magr->mhpmax) {
1693 healmon(magr, (abs(oldhpmax - u.uhpmax) + 1) / 2, 0);
1695 return TRUE;
1698 return FALSE;
1701 RESTORE_WARNING_FORMAT_NONLITERAL
1703 /* getobj callback for object to be invoked */
1704 staticfn int
1705 invoke_ok(struct obj *obj)
1707 if (!obj)
1708 return GETOBJ_EXCLUDE;
1710 /* artifacts and other special items */
1711 if (obj->oartifact || objects[obj->otyp].oc_unique
1712 || (obj->otyp == FAKE_AMULET_OF_YENDOR && !obj->known))
1713 return GETOBJ_SUGGEST;
1715 /* synonym for apply, though actually invoking it will do different things
1716 * depending if it's a regular crystal ball, an artifact one that has an
1717 * invoke power, and a (theoretical) artifact one that doesn't have an
1718 * invoke power */
1719 if (obj->otyp == CRYSTAL_BALL)
1720 return GETOBJ_SUGGEST;
1722 return GETOBJ_EXCLUDE;
1725 /* the #invoke command */
1727 doinvoke(void)
1729 struct obj *obj;
1731 obj = getobj("invoke", invoke_ok, GETOBJ_PROMPT);
1732 if (!obj)
1733 return ECMD_CANCEL;
1734 if (!retouch_object(&obj, FALSE))
1735 return ECMD_TIME;
1736 return arti_invoke(obj);
1739 staticfn void
1740 nothing_special(struct obj *obj)
1742 if (carried(obj))
1743 You_feel("a surge of power, but nothing seems to happen.");
1746 staticfn int
1747 arti_invoke(struct obj *obj)
1749 const struct artifact *oart;
1751 if (!obj) {
1752 impossible("arti_invoke without obj");
1753 return ECMD_OK;
1755 oart = get_artifact(obj);
1756 if (oart == &artilist[ART_NONARTIFACT] || !oart->inv_prop) {
1757 if (obj->otyp == CRYSTAL_BALL)
1758 use_crystal_ball(&obj);
1759 else
1760 pline1(nothing_happens);
1761 return ECMD_TIME;
1764 if (oart->inv_prop > LAST_PROP) {
1765 /* It's a special power, not "just" a property */
1766 if (obj->age > svm.moves) {
1767 /* the artifact is tired :-) */
1768 You_feel("that %s %s ignoring you.", the(xname(obj)),
1769 otense(obj, "are"));
1770 /* and just got more so; patience is essential... */
1771 obj->age += (long) d(3, 10);
1772 return ECMD_TIME;
1774 obj->age = svm.moves + rnz(100);
1776 switch (oart->inv_prop) {
1777 case TAMING: {
1778 struct obj pseudo;
1780 pseudo =
1781 cg.zeroobj; /* neither cursed nor blessed, zero oextra too */
1782 pseudo.otyp = SCR_TAMING;
1783 (void) seffects(&pseudo);
1784 break;
1786 case HEALING: {
1787 int healamt = (u.uhpmax + 1 - u.uhp) / 2;
1788 long creamed = (long) u.ucreamed;
1790 if (Upolyd)
1791 healamt = (u.mhmax + 1 - u.mh) / 2;
1792 if (healamt || Sick || Slimed || Blinded > creamed)
1793 You_feel("better.");
1794 if (healamt || Sick || Slimed || BlindedTimeout > creamed)
1795 You_feel("%sbetter.",
1796 (!healamt && !Sick && !Slimed
1797 /* when healing temporary blindness (aside from
1798 goop covering face), might still be blind
1799 due to PermaBlind or eyeless polymorph;
1800 vary the message in that situation */
1801 && (HBlinded & ~TIMEOUT) != 0L) ? "slightly " : "");
1802 else {
1803 nothing_special(obj);
1804 return ECMD_TIME;
1806 if (healamt > 0) {
1807 if (Upolyd)
1808 u.mh += healamt;
1809 else
1810 u.uhp += healamt;
1812 if (Sick)
1813 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
1814 if (Slimed)
1815 make_slimed(0L, (char *) 0);
1816 if (BlindedTimeout > creamed)
1817 make_blinded(creamed, FALSE);
1818 disp.botl = TRUE;
1819 break;
1821 case ENERGY_BOOST: {
1822 int epboost = (u.uenmax + 1 - u.uen) / 2;
1824 if (epboost > 120)
1825 epboost = 120; /* arbitrary */
1826 else if (epboost < 12)
1827 epboost = u.uenmax - u.uen;
1828 if (epboost) {
1829 u.uen += epboost;
1830 disp.botl = TRUE;
1831 You_feel("re-energized.");
1832 } else {
1833 nothing_special(obj);
1834 return ECMD_TIME;
1836 break;
1838 case UNTRAP: {
1839 if (!untrap(TRUE, 0, 0, (struct obj *) 0)) {
1840 obj->age = 0; /* don't charge for changing their mind */
1841 return ECMD_OK;
1843 break;
1845 case CHARGE_OBJ: {
1846 struct obj *otmp = getobj("charge", charge_ok,
1847 GETOBJ_PROMPT | GETOBJ_ALLOWCNT);
1848 boolean b_effect;
1850 if (!otmp) {
1851 obj->age = 0;
1852 return ECMD_CANCEL;
1854 b_effect = (obj->blessed && (oart->role == Role_switch
1855 || oart->role == NON_PM));
1856 recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0);
1857 update_inventory();
1858 break;
1860 case LEV_TELE:
1861 level_tele();
1862 break;
1863 case CREATE_PORTAL: {
1864 int i, num_ok_dungeons, last_ok_dungeon = 0;
1865 d_level newlev;
1866 winid tmpwin = create_nhwindow(NHW_MENU);
1867 anything any;
1868 int clr = NO_COLOR;
1870 any = cg.zeroany; /* set all bits to zero */
1871 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
1872 /* use index+1 (can't use 0) as identifier */
1873 for (i = num_ok_dungeons = 0; i < svn.n_dgns; i++) {
1874 if (!svd.dungeons[i].dunlev_ureached)
1875 continue;
1876 if (i == tutorial_dnum) /* can't portal into tutorial */
1877 continue;
1878 any.a_int = i + 1;
1879 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
1880 ATR_NONE, clr,
1881 svd.dungeons[i].dname, MENU_ITEMFLAGS_NONE);
1882 num_ok_dungeons++;
1883 last_ok_dungeon = i;
1885 end_menu(tmpwin, "Open a portal to which dungeon?");
1886 if (num_ok_dungeons > 1) {
1887 /* more than one entry; display menu for choices */
1888 menu_item *selected;
1889 int n;
1891 n = select_menu(tmpwin, PICK_ONE, &selected);
1892 if (n <= 0) {
1893 destroy_nhwindow(tmpwin);
1894 nothing_special(obj);
1895 return ECMD_TIME;
1897 i = selected[0].item.a_int - 1;
1898 free((genericptr_t) selected);
1899 } else
1900 i = last_ok_dungeon; /* also first & only OK dungeon */
1901 destroy_nhwindow(tmpwin);
1904 * i is now index into dungeon structure for the new dungeon.
1905 * Find the closest level in the given dungeon, open
1906 * a use-once portal to that dungeon and go there.
1907 * The closest level is either the entry or dunlev_ureached.
1909 newlev.dnum = i;
1910 if (svd.dungeons[i].depth_start >= depth(&u.uz))
1911 newlev.dlevel = svd.dungeons[i].entry_lev;
1912 else
1913 newlev.dlevel = svd.dungeons[i].dunlev_ureached;
1915 if (u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev)
1916 || newlev.dnum == u.uz.dnum || !next_to_u()) {
1917 You_feel("very disoriented for a moment.");
1918 } else {
1919 if (!Blind)
1920 You("are surrounded by a shimmering sphere!");
1921 else
1922 You_feel("weightless for a moment.");
1923 goto_level(&newlev, FALSE, FALSE, FALSE);
1925 break;
1927 case ENLIGHTENING:
1928 enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS);
1929 break;
1930 case CREATE_AMMO: {
1931 struct obj *otmp = mksobj(ARROW, TRUE, FALSE);
1933 if (!otmp) {
1934 nothing_special(obj);
1935 return ECMD_TIME;
1937 otmp->blessed = obj->blessed;
1938 otmp->cursed = obj->cursed;
1939 otmp->bknown = obj->bknown;
1940 otmp->oeroded = otmp->oeroded2 = 0;
1941 if (obj->blessed) {
1942 if (otmp->spe < 0)
1943 otmp->spe = 0;
1944 otmp->quan += rnd(10);
1945 } else if (obj->cursed) {
1946 if (otmp->spe > 0)
1947 otmp->spe = 0;
1948 } else
1949 otmp->quan += rnd(5);
1950 otmp->owt = weight(otmp);
1951 otmp = hold_another_object(otmp, "Suddenly %s out.",
1952 aobjnam(otmp, "fall"), (char *) 0);
1953 nhUse(otmp);
1954 break;
1956 case BANISH: {
1957 int nvanished = 0, nstayed = 0;
1958 struct monst *mtmp, *mtmp2;
1959 d_level dest;
1961 find_hell(&dest);
1963 for (mtmp = fmon; mtmp; mtmp = mtmp2) {
1964 int chance = 1;
1966 mtmp2 = mtmp->nmon;
1967 if (DEADMONSTER(mtmp) || !isok(mtmp->mx, mtmp->my))
1968 continue;
1969 if (!is_demon(mtmp->data) && mtmp->data->mlet != S_IMP)
1970 continue;
1971 if (!couldsee(mtmp->mx, mtmp->my))
1972 continue;
1973 if (mtmp->data->msound == MS_NEMESIS)
1974 continue;
1976 if (In_quest(&u.uz) && !svq.quest_status.killed_nemesis)
1977 chance += 10;
1978 if (is_dprince(mtmp->data))
1979 chance += 2;
1980 if (is_dlord(mtmp->data))
1981 chance++;
1983 mtmp->msleeping = mtmp->mtame = mtmp->mpeaceful = 0;
1984 if (chance <= 1 || !rn2(chance)) {
1985 if (!Inhell) {
1986 nvanished++;
1987 /* banish to a random level in Gehennom */
1988 dest.dlevel = rn2(dunlevs_in_dungeon(&dest));
1989 migrate_mon(mtmp, ledger_no(&dest), MIGR_RANDOM);
1990 } else {
1991 u_teleport_mon(mtmp, FALSE);
1993 } else {
1994 nstayed++;
1998 if (nvanished) {
1999 char subject[] = "demons";
2001 if (nvanished == 1)
2002 *(eos(subject) - 1) = '\0'; /* remove 's' */
2003 pline("%s %s %s in a cloud of brimstone!",
2004 nstayed ? ((nvanished > nstayed)
2005 ? "Most of the"
2006 : "Some of the")
2007 : "The",
2008 subject, vtense(subject, "disappear"));
2010 break;
2012 case BLINDING_RAY:
2013 if (getdir((char *) 0)) {
2014 if (u.dx || u.dy) {
2015 do_blinding_ray(obj);
2016 } else if (u.dz) {
2017 /* up or down => light this map spot; litroom() uses
2018 radius 0 for Sunsword, except on Rogue level where
2019 whole room gets lit and corridor spots remain unlit */
2020 litroom(TRUE, obj);
2021 pline("%s", ((!Blind && levl[u.ux][u.uy].lit
2022 && !levl[u.ux][u.uy].waslit)
2023 ? "It is lit here now."
2024 : nothing_seems_to_happen));
2025 } else { /* zapyourself() */
2026 boolean vulnerable = (u.umonnum == PM_GREMLIN);
2027 int damg = obj->blessed ? 15 : !obj->cursed ? 10 : 5;
2029 if (vulnerable) /* could be fatal if Unchanging */
2030 (void) lightdamage(obj, TRUE, 2 * damg);
2032 if (!flashburn((long) (damg + rnd(damg)), FALSE)
2033 && !vulnerable)
2034 pline("%s", nothing_seems_to_happen);
2036 } else {
2037 /* no direction picked */
2038 pline("%s", Never_mind);
2039 obj->age = svm.moves;
2041 break;
2042 default:
2043 impossible("Unknown invoke power %d.", oart->inv_prop);
2044 break;
2046 } else {
2047 long eprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI),
2048 iprop = u.uprops[oart->inv_prop].intrinsic;
2049 boolean on = (eprop & W_ARTI) != 0; /* true if prop just set */
2051 if (on && obj->age > svm.moves) {
2052 /* the artifact is tired :-) */
2053 u.uprops[oart->inv_prop].extrinsic ^= W_ARTI;
2054 You_feel("that %s %s ignoring you.", the(xname(obj)),
2055 otense(obj, "are"));
2056 /* can't just keep repeatedly trying */
2057 obj->age += (long) d(3, 10);
2058 return ECMD_TIME;
2059 } else if (!on) {
2060 /* when turning off property, determine downtime */
2061 /* arbitrary for now until we can tune this -dlc */
2062 obj->age = svm.moves + rnz(100);
2065 if ((eprop & ~W_ARTI) || iprop) {
2066 /* you had the property from some other source too */
2067 nothing_special(obj);
2068 return ECMD_TIME;
2070 switch (oart->inv_prop) {
2071 case CONFLICT:
2072 if (on)
2073 You_feel("like a rabble-rouser.");
2074 else
2075 You_feel("the tension decrease around you.");
2076 break;
2077 case LEVITATION:
2078 if (on) {
2079 float_up();
2080 spoteffects(FALSE);
2081 } else
2082 (void) float_down(I_SPECIAL | TIMEOUT, W_ARTI);
2083 break;
2084 case INVIS:
2085 if (BInvis || Blind) {
2086 nothing_special(obj);
2087 return ECMD_TIME;
2089 newsym(u.ux, u.uy);
2090 if (on)
2091 Your("body takes on a %s transparency...",
2092 Hallucination ? "normal" : "strange");
2093 else
2094 Your("body seems to unfade...");
2095 break;
2099 return ECMD_TIME;
2102 /* will freeing this object from inventory cause levitation to end? */
2103 boolean
2104 finesse_ahriman(struct obj *obj)
2106 const struct artifact *oart;
2107 struct prop save_Lev;
2108 boolean result;
2110 /* if we aren't levitating or this isn't an artifact which confers
2111 levitation via #invoke then freeinv() won't toggle levitation */
2112 if (!Levitation
2113 || (oart = get_artifact(obj)) == &artilist[ART_NONARTIFACT]
2114 || oart->inv_prop != LEVITATION || !(ELevitation & W_ARTI))
2115 return FALSE;
2117 /* arti_invoke(off) -> float_down() clears I_SPECIAL|TIMEOUT & W_ARTI;
2118 probe ahead to see whether that actually results in floating down;
2119 (this assumes that there aren't two simultaneously invoked artifacts
2120 both conferring levitation--safe, since if there were two of them,
2121 invoking the 2nd would negate the 1st rather than stack with it) */
2122 save_Lev = u.uprops[LEVITATION];
2123 HLevitation &= ~(I_SPECIAL | TIMEOUT);
2124 ELevitation &= ~W_ARTI;
2125 result = (boolean) !Levitation;
2126 u.uprops[LEVITATION] = save_Lev;
2127 return result;
2130 /* WAC return TRUE if artifact is always lit */
2131 boolean
2132 artifact_light(struct obj *obj)
2134 /* not artifacts but treat them as if they were because they emit
2135 light without burning */
2136 if (obj && (obj->otyp == GOLD_DRAGON_SCALE_MAIL
2137 || obj->otyp == GOLD_DRAGON_SCALES)
2138 && (obj->owornmask & W_ARM) != 0L)
2139 return TRUE;
2141 return (boolean) ((get_artifact(obj) != &artilist[ART_NONARTIFACT])
2142 && is_art(obj, ART_SUNSWORD));
2145 /* KMH -- Talking artifacts are finally implemented */
2147 arti_speak(struct obj *obj)
2149 const struct artifact *oart = get_artifact(obj);
2150 const char *line;
2151 char buf[BUFSZ];
2153 /* Is this a speaking artifact? */
2154 if (oart == &artilist[ART_NONARTIFACT] || !(oart->spfx & SPFX_SPEAK))
2155 return ECMD_OK; /* nothing happened */
2157 line = getrumor(bcsign(obj), buf, TRUE);
2158 if (!*line)
2159 line = "NetHack rumors file closed for renovation.";
2160 pline("%s:", Tobjnam(obj, "whisper"));
2161 SetVoice((struct monst *) 0, 0, 80, voice_talking_artifact);
2162 verbalize1(line);
2163 return ECMD_TIME;
2166 boolean
2167 artifact_has_invprop(struct obj *otmp, uchar inv_prop)
2169 const struct artifact *arti = get_artifact(otmp);
2171 return (boolean) ((arti != &artilist[ART_NONARTIFACT])
2172 && (arti->inv_prop == inv_prop));
2175 /* Return the price sold to the hero of a given artifact or unique item */
2176 long
2177 arti_cost(struct obj *otmp)
2179 if (!otmp->oartifact)
2180 return (long) objects[otmp->otyp].oc_cost;
2181 else if (artilist[(int) otmp->oartifact].cost)
2182 return artilist[(int) otmp->oartifact].cost;
2183 else
2184 return (100L * (long) objects[otmp->otyp].oc_cost);
2187 staticfn uchar
2188 abil_to_adtyp(long *abil)
2190 struct abil2adtyp_tag {
2191 long *abil;
2192 uchar adtyp;
2193 } abil2adtyp[] = {
2194 { &EFire_resistance, AD_FIRE },
2195 { &ECold_resistance, AD_COLD },
2196 { &EShock_resistance, AD_ELEC },
2197 { &EAntimagic, AD_MAGM },
2198 { &EDisint_resistance, AD_DISN },
2199 { &EPoison_resistance, AD_DRST },
2200 { &EDrain_resistance, AD_DRLI },
2202 int k;
2204 for (k = 0; k < SIZE(abil2adtyp); k++) {
2205 if (abil2adtyp[k].abil == abil)
2206 return abil2adtyp[k].adtyp;
2208 return 0;
2211 staticfn unsigned long
2212 abil_to_spfx(long *abil)
2214 static const struct abil2spfx_tag {
2215 long *abil;
2216 unsigned long spfx;
2217 } abil2spfx[] = {
2218 { &ESearching, SPFX_SEARCH },
2219 { &EHalluc_resistance, SPFX_HALRES },
2220 { &ETelepat, SPFX_ESP },
2221 { &EStealth, SPFX_STLTH },
2222 { &ERegeneration, SPFX_REGEN },
2223 { &ETeleport_control, SPFX_TCTRL },
2224 { &EWarn_of_mon, SPFX_WARN },
2225 { &EWarning, SPFX_WARN },
2226 { &EEnergy_regeneration, SPFX_EREGEN },
2227 { &EHalf_spell_damage, SPFX_HSPDAM },
2228 { &EHalf_physical_damage, SPFX_HPHDAM },
2229 { &EReflecting, SPFX_REFLECT },
2231 int k;
2233 for (k = 0; k < SIZE(abil2spfx); k++) {
2234 if (abil2spfx[k].abil == abil)
2235 return abil2spfx[k].spfx;
2237 return 0L;
2241 * Return the first item that is conveying a particular intrinsic.
2243 struct obj *
2244 what_gives(long *abil)
2246 struct obj *obj;
2247 uchar dtyp;
2248 unsigned long spfx;
2249 long wornbits;
2250 long wornmask = (W_ARM | W_ARMC | W_ARMH | W_ARMS
2251 | W_ARMG | W_ARMF | W_ARMU
2252 | W_AMUL | W_RINGL | W_RINGR | W_TOOL
2253 | W_ART | W_ARTI);
2255 if (u.twoweap)
2256 wornmask |= W_SWAPWEP;
2257 dtyp = abil_to_adtyp(abil);
2258 spfx = abil_to_spfx(abil);
2259 wornbits = (wornmask & *abil);
2261 for (obj = gi.invent; obj; obj = obj->nobj) {
2262 if (obj->oartifact
2263 && (abil != &EWarn_of_mon || svc.context.warntype.obj)) {
2264 const struct artifact *art = get_artifact(obj);
2266 if (art != &artilist[ART_NONARTIFACT]) {
2267 if (dtyp) {
2268 if (art->cary.adtyp == dtyp /* carried */
2269 || (art->defn.adtyp == dtyp /* defends while worn */
2270 && (obj->owornmask & ~(W_ART | W_ARTI))))
2271 return obj;
2273 if (spfx) {
2274 /* property conferred when carried */
2275 if ((art->cspfx & spfx) == spfx)
2276 return obj;
2277 /* property conferred when wielded or worn */
2278 if ((art->spfx & spfx) == spfx && obj->owornmask)
2279 return obj;
2281 if (obj == uwep && abil == &EBlnd_resist
2282 && (*abil & W_WEP) != 0L) {
2283 return obj; /* Sunsword */
2286 } else {
2287 if (wornbits && wornbits == (wornmask & obj->owornmask))
2288 return obj;
2291 return (struct obj *) 0;
2294 const char *
2295 glow_color(int arti_indx)
2297 int colornum = artilist[arti_indx].acolor;
2298 const char *colorstr = clr2colorname(colornum);
2300 return hcolor(colorstr);
2303 /* glow verb; [0] holds the value used when blind */
2304 static const char *const glow_verbs[] = {
2305 "quiver", "flicker", "glimmer", "gleam"
2308 /* relative strength that Sting is glowing (0..3), to select verb */
2309 staticfn int
2310 glow_strength(int count)
2312 /* glow strength should also be proportional to proximity and
2313 probably difficulty, but we don't have that information and
2314 gathering it is more trouble than this would be worth */
2315 return (count > 12) ? 3 : (count > 4) ? 2 : (count > 0);
2318 const char *
2319 glow_verb(int count, /* 0 means blind rather than no applicable creatures */
2320 boolean ingsfx)
2322 static char resbuf[20];
2324 Strcpy(resbuf, glow_verbs[glow_strength(count)]);
2325 /* ing_suffix() will double the last consonant for all the words
2326 we're using and none of them should have that, so bypass it */
2327 if (ingsfx)
2328 Strcat(resbuf, "ing");
2329 return resbuf;
2332 /* use for warning "glow" for Sting, Orcrist, and Grimtooth */
2333 void
2334 Sting_effects(
2335 int orc_count) /* new count (warn_obj_cnt is old count);
2336 * -1 is a flag value */
2338 if (u_wield_art(ART_STING)
2339 || u_wield_art(ART_ORCRIST)
2340 || u_wield_art(ART_GRIMTOOTH)) {
2341 int oldstr = glow_strength(gw.warn_obj_cnt),
2342 newstr = glow_strength(orc_count);
2344 if (orc_count == -1 && gw.warn_obj_cnt > 0) {
2345 /* -1 means that blindness has just been toggled; give a
2346 'continue' message that eventual 'stop' message will match */
2347 pline("%s is %s.", bare_artifactname(uwep),
2348 glow_verb(Blind ? 0 : gw.warn_obj_cnt, TRUE));
2349 } else if (newstr > 0 && newstr != oldstr) {
2350 /* goto_level() -> docrt() -> see_monsters() -> Sting_effects();
2351 if "you materialize on a different level" is pending, give
2352 it now so that start-glowing message comes after it */
2353 maybe_lvltport_feedback(); /* usually called by goto_level() */
2355 /* 'start' message */
2356 if (!Blind)
2357 pline("%s %s %s%c", bare_artifactname(uwep),
2358 otense(uwep, glow_verb(orc_count, FALSE)),
2359 glow_color(uwep->oartifact),
2360 (newstr > oldstr) ? '!' : '.');
2361 else if (oldstr == 0) /* quivers */
2362 pline("%s %s slightly.", bare_artifactname(uwep),
2363 otense(uwep, glow_verb(0, FALSE)));
2364 } else if (orc_count == 0 && gw.warn_obj_cnt > 0) {
2365 /* 'stop' message */
2366 pline("%s stops %s.", bare_artifactname(uwep),
2367 glow_verb(Blind ? 0 : gw.warn_obj_cnt, TRUE));
2372 /* called when hero is wielding/applying/invoking a carried item, or
2373 after undergoing a transformation (alignment change, lycanthropy,
2374 polymorph) which might affect item access */
2376 retouch_object(
2377 struct obj **objp, /* might be destroyed or unintentionally dropped */
2378 boolean loseit) /* whether to drop it if hero can longer touch it */
2380 struct obj *obj = *objp;
2382 /* allow hero in silver-hating form to try to perform invocation ritual */
2383 if (obj->otyp == BELL_OF_OPENING
2384 && invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
2385 return 1;
2388 if (touch_artifact(obj, &gy.youmonst)) {
2389 char buf[BUFSZ];
2390 int dmg = 0, tmp;
2391 boolean ag = (objects[obj->otyp].oc_material == SILVER && Hate_silver),
2392 bane = bane_applies(get_artifact(obj), &gy.youmonst);
2394 /* nothing else to do if hero can successfully handle this object */
2395 if (!ag && !bane)
2396 return 1;
2398 /* hero can't handle this object, but didn't get touch_artifact()'s
2399 "<obj> evades your grasp|control" message; give an alternate one */
2400 You_cant("handle %s%s!", yname(obj),
2401 obj->owornmask ? " anymore" : "");
2402 /* also inflict damage unless touch_artifact() already did so */
2403 if (!touch_blasted) {
2404 const char *what = killer_xname(obj);
2406 if (ag && !obj->oartifact && !bane) {
2407 /* 'obj' is silver; for rings and wands it ended up that
2408 way due to randomization at start of game; showing this
2409 game's silver item without stating that it is silver
2410 potentially leads to confusion about cause of death */
2411 if (obj->oclass == RING_CLASS)
2412 what = "a silver ring";
2413 else if (obj->oclass == WAND_CLASS)
2414 what = "a silver wand";
2415 /* for anything else, stick with killer_xname() */
2417 /* damage is somewhat arbitrary; half the usual 1d20 physical
2418 for silver, 1d10 magical for <foo>bane, potentially both */
2419 if (ag)
2420 tmp = rnd(10), dmg += Maybe_Half_Phys(tmp);
2421 if (bane)
2422 dmg += rnd(10);
2423 Sprintf(buf, "handling %s", what);
2424 losehp(dmg, buf, KILLED_BY);
2425 exercise(A_CON, FALSE);
2429 /* removing a worn item might result in loss of levitation,
2430 dropping the hero onto a polymorph trap or into water or
2431 lava and potentially dropping or destroying the item */
2432 if (obj->owornmask) {
2433 struct obj *otmp;
2435 remove_worn_item(obj, FALSE);
2436 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
2437 if (otmp == obj)
2438 break;
2439 if (!otmp)
2440 *objp = obj = 0;
2443 /* if we still have it and caller wants us to drop it, do so now */
2444 if (loseit && obj) {
2445 if (Levitation) {
2446 freeinv(obj);
2447 hitfloor(obj, TRUE);
2448 } else {
2449 /* dropx gives a message if a dropped item lands on an altar;
2450 we provide one for other terrain */
2451 if (!IS_ALTAR(levl[u.ux][u.uy].typ))
2452 pline("%s to the %s.", Tobjnam(obj, "fall"),
2453 surface(u.ux, u.uy));
2454 dropx(obj);
2456 *objp = obj = 0; /* no longer in inventory */
2458 return 0;
2461 /* hero has changed form or alignment; an item which is worn/wielded
2462 or an artifact which conveys something via being carried or which
2463 has an #invoke effect currently in operation undergoes a touch test;
2464 if it fails, it will be unworn/unwielded and maybe dropped */
2465 staticfn boolean
2466 untouchable(
2467 struct obj *obj, /* object to test; in invent or is steed's saddle */
2468 boolean drop_untouchable) /* whether to drop it if it can't be touched */
2470 struct artifact *art;
2471 boolean beingworn, carryeffect, invoked;
2472 long wearmask = ~(W_QUIVER | (u.twoweap ? 0L : W_SWAPWEP) | W_BALL);
2474 beingworn = (obj /* never Null; this pacifies static analysis when
2475 * the get_artifact() macro tests 'obj' for Null */
2476 && ((obj->owornmask & wearmask) != 0L
2477 /* some items in use don't have any wornmask setting */
2478 || (obj->oclass == TOOL_CLASS
2479 && (obj->lamplit
2480 || (obj->otyp == LEASH && obj->leashmon)
2481 || (Is_container(obj) && Has_contents(obj))))));
2483 if ((art = get_artifact(obj)) != &artilist[ART_NONARTIFACT]) {
2484 carryeffect = (art->cary.adtyp || art->cspfx);
2485 invoked = (art->inv_prop > 0 && art->inv_prop <= LAST_PROP
2486 && (u.uprops[art->inv_prop].extrinsic & W_ARTI) != 0L);
2487 } else {
2488 carryeffect = invoked = FALSE;
2491 if (beingworn || carryeffect || invoked) {
2492 if (!retouch_object(&obj, drop_untouchable)) {
2493 /* "<artifact> is beyond your control" or "you can't handle
2494 <object>" has been given and it is now unworn/unwielded
2495 and possibly dropped (depending upon caller); if dropped,
2496 carried effect was turned off, else we leave that alone;
2497 we turn off invocation property here if still carried */
2498 if (invoked && obj)
2499 (void) arti_invoke(obj); /* reverse #invoke */
2500 return TRUE;
2503 return FALSE;
2506 /* check all items currently in use (mostly worn) for touchability */
2507 void
2508 retouch_equipment(
2509 int dropflag) /* 0==don't drop, 1==drop all, 2==drop weapon */
2511 static int nesting = 0; /* recursion control */
2512 struct obj *obj;
2513 boolean dropit, had_gloves = (uarmg != 0);
2514 int had_rings = (!!uleft + !!uright);
2517 * We can potentially be called recursively if losing/unwearing
2518 * an item causes worn helm of opposite alignment to come off or
2519 * be destroyed.
2521 * BUG: if the initial call was due to putting on a helm of
2522 * opposite alignment and it does come off to trigger recursion,
2523 * after the inner call executes, the outer call will finish
2524 * using the non-helm alignment rather than the helm alignment
2525 * which triggered this in the first place.
2527 if (!nesting++)
2528 clear_bypasses(); /* init upon initial entry */
2530 dropit = (dropflag > 0); /* drop all or drop weapon */
2531 /* check secondary weapon first, before possibly unwielding primary */
2532 if (u.twoweap) {
2533 bypass_obj(uswapwep); /* so loop below won't process it again */
2534 (void) untouchable(uswapwep, dropit);
2536 /* check primary weapon next so that they're handled together */
2537 if (uwep) {
2538 bypass_obj(uwep); /* so loop below won't process it again */
2539 (void) untouchable(uwep, dropit);
2542 /* in case someone is daft enough to add artifact or silver saddle */
2543 if (u.usteed && (obj = which_armor(u.usteed, W_SADDLE)) != 0) {
2544 /* untouchable() calls retouch_object() which expects an object in
2545 hero's inventory, but remove_worn_item() will be harmless for
2546 saddle and we're suppressing drop, so this works as intended */
2547 if (untouchable(obj, FALSE))
2548 dismount_steed(DISMOUNT_THROWN);
2551 * TODO? Force off gloves if either or both rings are going to
2552 * become unworn; force off cloak [suit] before suit [shirt].
2553 * The torso handling is hypothetical; the case for gloves is
2554 * not, due to the possibility of unwearing silver rings.
2557 dropit = (dropflag == 1); /* all untouchable items */
2558 /* loss of levitation (silver ring, or Heart of Ahriman invocation)
2559 might cause hero to lose inventory items (by dropping into lava,
2560 for instance), so inventory traversal needs to rescan the whole
2561 gi.invent chain each time it moves on to another object; we use bypass
2562 handling to keep track of which items have already been processed */
2563 while ((obj = nxt_unbypassed_obj(gi.invent)) != 0)
2564 (void) untouchable(obj, dropit);
2566 if (had_rings != (!!uleft + !!uright) && uarmg && uarmg->cursed)
2567 uncurse(uarmg); /* temporary? hack for ring removal plausibility */
2568 if (had_gloves && !uarmg)
2569 selftouch("After losing your gloves, you");
2571 if (!--nesting)
2572 clear_bypasses(); /* reset upon final exit */
2575 staticfn int
2576 count_surround_traps(coordxy x, coordxy y)
2578 struct rm *levp;
2579 struct obj *o;
2580 coordxy dx, dy;
2581 int glyph, ret = 0;
2583 for (dx = x - 1; dx < x + 2; ++dx)
2584 for (dy = y - 1; dy < y + 2; ++dy) {
2585 if (!isok(dx, dy))
2586 continue;
2587 /* If a trap is shown here, don't count it; the hero
2588 * should be expecting it. But if there is a trap here
2589 * that's not shown, either undiscovered or covered by
2590 * something, do count it.
2592 glyph = glyph_at(dx, dy);
2593 if (glyph_is_trap(glyph))
2594 continue;
2595 if (t_at(dx, dy)) {
2596 ++ret;
2597 continue;
2599 levp = &levl[dx][dy];
2600 if (IS_DOOR(levp->typ) && (levp->doormask & D_TRAPPED) != 0) {
2601 ++ret;
2602 continue;
2604 for (o = svl.level.objects[dx][dy]; o; o = o->nexthere)
2605 if (Is_container(o) && o->otrapped) {
2606 ++ret; /* we're counting locations, so just */
2607 break; /* count the first one in a pile */
2611 * [Shouldn't we also check inventory for a trapped container?
2612 * Even if its trap has already been found, there's no 'tknown'
2613 * flag to help hero remember that so we have nothing comparable
2614 * to a shown glyph to justify skipping it.]
2616 return ret;
2619 /* sense adjacent traps if wielding MKoT without wearing gloves */
2620 void
2621 mkot_trap_warn(void)
2623 static const char *const heat[7] = {
2624 "cool", "slightly warm", "warm", "very warm",
2625 "hot", "very hot", "like fire"
2628 if (!uarmg && u_wield_art(ART_MASTER_KEY_OF_THIEVERY)) {
2629 int idx, ntraps = count_surround_traps(u.ux, u.uy);
2631 if (ntraps != gm.mkot_trap_warn_count) {
2632 idx = min(ntraps, SIZE(heat) - 1);
2633 pline_The("Key feels %s%c", heat[idx], (ntraps > 3) ? '!' : '.');
2635 gm.mkot_trap_warn_count = ntraps;
2636 } else
2637 gm.mkot_trap_warn_count = 0;
2640 /* Master Key is magic key if its bless/curse state meets our criteria:
2641 not cursed for rogues or blessed for non-rogues */
2642 boolean
2643 is_magic_key(struct monst *mon, /* if null, non-rogue is assumed */
2644 struct obj *obj)
2646 if (is_art(obj, ART_MASTER_KEY_OF_THIEVERY)) {
2647 if ((mon == &gy.youmonst) ? Role_if(PM_ROGUE)
2648 : (mon && mon->data == &mons[PM_ROGUE]))
2649 return !obj->cursed; /* a rogue; non-cursed suffices for magic */
2650 /* not a rogue; key must be blessed to behave as a magic one */
2651 return obj->blessed;
2653 return FALSE;
2656 /* figure out whether 'mon' (usually youmonst) is carrying the magic key */
2657 struct obj *
2658 has_magic_key(struct monst *mon) /* if null, hero assumed */
2660 struct obj *o;
2661 short key = artilist[ART_MASTER_KEY_OF_THIEVERY].otyp;
2663 if (!mon)
2664 mon = &gy.youmonst;
2665 for (o = ((mon == &gy.youmonst) ? gi.invent : mon->minvent); o;
2666 o = nxtobj(o, key, FALSE)) {
2667 if (is_magic_key(mon, o))
2668 return o;
2670 return (struct obj *) 0;
2673 /* #define is_art(o,art) ((o) && (o)->oartifact == (art)) */
2675 boolean
2676 is_art(struct obj *obj, int art)
2678 if (obj && obj->oartifact == art)
2679 return TRUE;
2680 return FALSE;
2683 /* #define get_artifact(o) \
2684 (((o) && ((o)->artifact > 0 && (o)->artifact < AFTER_LAST_ARTIFACT)) \
2685 ? &artilist[(int) (o)->oartifact] \
2686 : &artilist[ART_NONARTIFACT]) */
2688 staticfn struct artifact *
2689 get_artifact(struct obj *obj)
2691 if (obj) {
2692 int artidx = (int) obj->oartifact;
2694 /* skip 0, 1st artifact at 1 */
2695 /* SIZE(artilist) would include the terminator,
2696 so use AFTER_LAST_ARTIFACT instead */
2697 if (artidx > 0 && artidx < AFTER_LAST_ARTIFACT)
2698 return &artilist[artidx];
2700 return &artilist[ART_NONARTIFACT];
2702 /*artifact.c*/