1 /* NetHack 3.7	artifact.c	$NHDT-Date: 1620326528 2021/05/06 18:42:08 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.167 $ */
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. */
5 
6 #include "hack.h"
7 #include "artifact.h"
8 #include "artilist.h"
9 
10 /*
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.
15  */
16 
17 #define get_artifact(o) \
18     (((o) && (o)->oartifact) ? &artilist[(int) (o)->oartifact] : 0)
19 
20 static boolean bane_applies(const struct artifact *, struct monst *);
21 static int spec_applies(const struct artifact *, struct monst *);
22 static int invoke_ok(struct obj *);
23 static int arti_invoke(struct obj *);
24 static boolean Mb_hit(struct monst * magr, struct monst *mdef,
25                       struct obj *, int *, int, boolean, char *);
26 static unsigned long abil_to_spfx(long *);
27 static uchar abil_to_adtyp(long *);
28 static int glow_strength(int);
29 static boolean untouchable(struct obj *, boolean);
30 static int count_surround_traps(int, int);
31 
32 /* The amount added to the victim's total hit points to insure that the
33    victim will be killed even after damage bonus/penalty adjustments.
34    Most such penalties are small, and 200 is plenty; the exception is
35    half physical damage.  3.3.1 and previous versions tried to use a very
36    large number to account for this case; now, we just compute the fatal
37    damage by adding it to 2 times the total hit points instead of 1 time.
38    Note: this will still break if they have more than about half the number
39    of hit points that will fit in a 15 bit integer. */
40 #define FATAL_DAMAGE_MODIFIER 200
41 
42 static void hack_artifacts(void);
43 static boolean attacks(int, struct obj *);
44 
45 /* handle some special cases; must be called after u_init() */
46 static void
hack_artifacts(void)47 hack_artifacts(void)
48 {
49     struct artifact *art;
50     int alignmnt = aligns[flags.initalign].value;
51 
52     /* Fix up the alignments of "gift" artifacts */
53     for (art = artilist + 1; art->otyp; art++)
54         if (art->role == Role_switch && art->alignment != A_NONE)
55             art->alignment = alignmnt;
56 
57     /* Excalibur can be used by any lawful character, not just knights */
58     if (!Role_if(PM_KNIGHT))
59         artilist[ART_EXCALIBUR].role = NON_PM;
60 
61     /* Fix up the quest artifact */
62     if (g.urole.questarti) {
63         artilist[g.urole.questarti].alignment = alignmnt;
64         artilist[g.urole.questarti].role = Role_switch;
65     }
66     return;
67 }
68 
69 /* zero out the artifact existence list */
70 void
init_artifacts(void)71 init_artifacts(void)
72 {
73     (void) memset((genericptr_t) g.artiexist, 0, sizeof g.artiexist);
74     (void) memset((genericptr_t) g.artidisco, 0, sizeof g.artidisco);
75     hack_artifacts();
76 }
77 
78 void
save_artifacts(NHFILE * nhfp)79 save_artifacts(NHFILE *nhfp)
80 {
81     if (nhfp->structlevel) {
82         bwrite(nhfp->fd, (genericptr_t) g.artiexist, sizeof g.artiexist);
83         bwrite(nhfp->fd, (genericptr_t) g.artidisco, sizeof g.artidisco);
84     }
85 }
86 
87 void
restore_artifacts(NHFILE * nhfp)88 restore_artifacts(NHFILE *nhfp)
89 {
90     if (nhfp->structlevel) {
91         mread(nhfp->fd, (genericptr_t) g.artiexist, sizeof g.artiexist);
92         mread(nhfp->fd, (genericptr_t) g.artidisco, sizeof g.artidisco);
93     }
94     hack_artifacts();	/* redo non-saved special cases */
95 }
96 
97 const char *
artiname(int artinum)98 artiname(int artinum)
99 {
100     if (artinum <= 0 || artinum > NROFARTIFACTS)
101         return "";
102     return artilist[artinum].name;
103 }
104 
105 /*
106    Make an artifact.  If a specific alignment is specified, then an object of
107    the appropriate alignment is created from scratch, or 0 is returned if
108    none is available.  (If at least one aligned artifact has already been
109    given, then unaligned ones also become eligible for this.)
110    If no alignment is given, then 'otmp' is converted
111    into an artifact of matching type, or returned as-is if that's not
112    possible.
113    For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE);''
114    for the 1st, ``obj = mk_artifact((struct obj *)0, some_alignment);''.
115  */
116 struct obj *
mk_artifact(struct obj * otmp,aligntyp alignment)117 mk_artifact(struct obj *otmp,   /* existing object; ignored if alignment specified */
118             aligntyp alignment) /* target alignment, or A_NONE */
119 {
120     const struct artifact *a;
121     int m, n, altn;
122     boolean by_align = (alignment != A_NONE);
123     short o_typ = (by_align || !otmp) ? 0 : otmp->otyp;
124     boolean unique = !by_align && otmp && objects[o_typ].oc_unique;
125     short eligible[NROFARTIFACTS];
126 
127     n = altn = 0;    /* no candidates found yet */
128     eligible[0] = 0; /* lint suppression */
129     /* gather eligible artifacts */
130     for (m = 1, a = &artilist[m]; a->otyp; a++, m++) {
131         if (g.artiexist[m])
132             continue;
133         if ((a->spfx & SPFX_NOGEN) || unique)
134             continue;
135 
136         if (!by_align) {
137             /* looking for a particular type of item; not producing a
138                divine gift so we don't care about role's first choice */
139             if (a->otyp == o_typ)
140                 eligible[n++] = m;
141             continue; /* move on to next possibility */
142         }
143 
144         /* we're looking for an alignment-specific item
145            suitable for hero's role+race */
146         if ((a->alignment == alignment || a->alignment == A_NONE)
147             /* avoid enemies' equipment */
148             && (a->race == NON_PM || !race_hostile(&mons[a->race]))) {
149             /* when a role-specific first choice is available, use it */
150             if (Role_if(a->role)) {
151                 /* make this be the only possibility in the list */
152                 eligible[0] = m;
153                 n = 1;
154                 break; /* skip all other candidates */
155             }
156             /* found something to consider for random selection */
157             if (a->alignment != A_NONE || u.ugifts > 0) {
158                 /* right alignment, or non-aligned with at least 1
159                    previous gift bestowed, makes this one viable */
160                 eligible[n++] = m;
161             } else {
162                 /* non-aligned with no previous gifts;
163                    if no candidates have been found yet, record
164                    this one as a[nother] fallback possibility in
165                    case all aligned candidates have been used up
166                    (via wishing, naming, bones, random generation) */
167                 if (!n)
168                     eligible[altn++] = m;
169                 /* [once a regular candidate is found, the list
170                    is overwritten and `altn' becomes irrelevant] */
171             }
172         }
173     }
174 
175     /* resort to fallback list if main list was empty */
176     if (!n)
177         n = altn;
178 
179     if (n) {
180         /* found at least one candidate; pick one at random */
181         m = eligible[rn2(n)]; /* [0..n-1] */
182         a = &artilist[m];
183 
184         /* make an appropriate object if necessary, then christen it */
185         if (by_align)
186             otmp = mksobj((int) a->otyp, TRUE, FALSE);
187 
188         if (otmp) {
189             otmp = oname(otmp, a->name);
190             otmp->oartifact = m;
191             g.artiexist[m] = TRUE;
192         }
193     } else {
194         /* nothing appropriate could be found; return original object */
195         if (by_align)
196             otmp = 0; /* (there was no original object) */
197     }
198     /* poison artifacts that are permapoisoned */
199     if (permapoisoned(otmp))
200         otmp->opoisoned = 1;
201     return otmp;
202 }
203 
204 /*
205  * Returns the full name (with articles and correct capitalization) of an
206  * artifact named "name" if one exists, or NULL, it not.
207  * The given name must be rather close to the real name for it to match.
208  * The object type of the artifact is returned in otyp if the return value
209  * is non-NULL.
210  */
211 const char *
artifact_name(const char * name,short * otyp)212 artifact_name(const char *name, short *otyp)
213 {
214     register const struct artifact *a;
215     register const char *aname;
216 
217     if (!strncmpi(name, "the ", 4))
218         name += 4;
219 
220     for (a = artilist + 1; a->otyp; a++) {
221         aname = a->name;
222         if (!strncmpi(aname, "the ", 4))
223             aname += 4;
224         if (!strcmpi(name, aname)) {
225             *otyp = a->otyp;
226             return a->name;
227         }
228     }
229 
230     return (char *) 0;
231 }
232 
233 boolean
exist_artifact(int otyp,const char * name)234 exist_artifact(int otyp, const char *name)
235 {
236     register const struct artifact *a;
237     boolean *arex;
238 
239     if (otyp && *name)
240         for (a = artilist + 1, arex = g.artiexist + 1; a->otyp; a++, arex++)
241             if ((int) a->otyp == otyp && !strcmp(a->name, name))
242                 return *arex;
243     return FALSE;
244 }
245 
246 void
artifact_exists(struct obj * otmp,const char * name,boolean mod)247 artifact_exists(struct obj *otmp, const char *name, boolean mod)
248 {
249     register const struct artifact *a;
250 
251     if (otmp && *name)
252         for (a = artilist + 1; a->otyp; a++)
253             if (a->otyp == otmp->otyp && !strcmp(a->name, name)) {
254                 register int m = (int) (a - artilist);
255                 otmp->oartifact = (char) (mod ? m : 0);
256                 otmp->age = 0;
257                 if (otmp->otyp == RIN_INCREASE_DAMAGE)
258                     otmp->spe = 0;
259                 g.artiexist[m] = mod;
260                 break;
261             }
262     return;
263 }
264 
265 int
nartifact_exist(void)266 nartifact_exist(void)
267 {
268     int a = 0;
269     int n = SIZE(g.artiexist);
270 
271     while (n > 1)
272         if (g.artiexist[--n])
273             a++;
274 
275     return a;
276 }
277 
278 boolean
spec_ability(struct obj * otmp,unsigned long abil)279 spec_ability(struct obj *otmp, unsigned long abil)
280 {
281     const struct artifact *arti = get_artifact(otmp);
282 
283     return (boolean) (arti && (arti->spfx & abil) != 0L);
284 }
285 
286 /* used so that callers don't need to known about SPFX_ codes */
287 boolean
confers_luck(struct obj * obj)288 confers_luck(struct obj *obj)
289 {
290     /* might as well check for this too */
291     if (obj->otyp == LUCKSTONE)
292         return TRUE;
293 
294     return (boolean) (obj->oartifact && spec_ability(obj, SPFX_LUCK));
295 }
296 
297 /* used to check whether a monster is getting reflection from an artifact */
298 boolean
arti_reflects(struct obj * obj)299 arti_reflects(struct obj *obj)
300 {
301     const struct artifact *arti = get_artifact(obj);
302 
303     if (arti) {
304         /* while being worn */
305         if ((obj->owornmask & ~W_ART) && (arti->spfx & SPFX_REFLECT))
306             return TRUE;
307         /* just being carried */
308         if (arti->cspfx & SPFX_REFLECT)
309             return TRUE;
310     }
311     return FALSE;
312 }
313 
314 /* decide whether this obj is effective when attacking against shades or any
315  * other incorporeal monster */
316 boolean
shade_glare(struct obj * obj)317 shade_glare(struct obj *obj)
318 {
319     const struct artifact *arti;
320 
321     /* any silver object is effective; bone too, though it gets no bonus */
322     if (obj->material == SILVER || obj->material == BONE)
323         return TRUE;
324     /* non-silver artifacts with bonus against undead also are effective */
325     arti = get_artifact(obj);
326     if (arti && (arti->spfx & SPFX_DFLAG2) && arti->mtype == M2_UNDEAD)
327         return TRUE;
328     /* [if there was anything with special bonus against noncorporeals,
329        it would be effective too] */
330     /* otherwise, harmless to shades */
331     return FALSE;
332 }
333 
334 /* returns 1 if name is restricted for otmp->otyp */
335 boolean
restrict_name(struct obj * otmp,const char * name)336 restrict_name(struct obj *otmp, const char *name)
337 {
338     register const struct artifact *a;
339     const char *aname, *odesc, *other;
340     boolean sametype[NUM_OBJECTS];
341     int i, lo, hi, otyp = otmp->otyp, ocls = objects[otyp].oc_class;
342 
343     if (!*name)
344         return FALSE;
345     if (!strncmpi(name, "the ", 4))
346         name += 4;
347 
348     /* decide what types of objects are the same as otyp;
349        if it's been discovered, then only itself matches;
350        otherwise, include all other undiscovered objects
351        of the same class which have the same description
352        or share the same pool of shuffled descriptions */
353     (void) memset((genericptr_t) sametype, 0, sizeof sametype); /* FALSE */
354     sametype[otyp] = TRUE;
355     if (!objects[otyp].oc_name_known
356         && (odesc = OBJ_DESCR(objects[otyp])) != 0) {
357         obj_shuffle_range(otyp, &lo, &hi);
358         for (i = g.bases[ocls]; i < NUM_OBJECTS; i++) {
359             if (objects[i].oc_class != ocls)
360                 break;
361             if (!objects[i].oc_name_known
362                 && (other = OBJ_DESCR(objects[i])) != 0
363                 && (!strcmp(odesc, other) || (i >= lo && i <= hi)))
364                 sametype[i] = TRUE;
365         }
366     }
367 
368     /* Since almost every artifact is SPFX_RESTR, it doesn't cost
369        us much to do the string comparison before the spfx check.
370        Bug fix:  don't name multiple elven daggers "Sting".
371      */
372     for (a = artilist + 1; a->otyp; a++) {
373         if (!sametype[a->otyp])
374             continue;
375         aname = a->name;
376         if (!strncmpi(aname, "the ", 4))
377             aname += 4;
378         if (!strcmp(aname, name))
379             return (boolean) ((a->spfx & (SPFX_NOGEN | SPFX_RESTR)) != 0
380                               || otmp->quan > 1L);
381     }
382 
383     return FALSE;
384 }
385 
386 static boolean
attacks(int adtyp,struct obj * otmp)387 attacks(int adtyp, struct obj *otmp)
388 {
389     register const struct artifact *weap;
390 
391     if ((weap = get_artifact(otmp)) != 0)
392         return (boolean) (weap->attk.adtyp == adtyp);
393     return FALSE;
394 }
395 
396 boolean
defends(int adtyp,struct obj * otmp)397 defends(int adtyp, struct obj *otmp)
398 {
399     register const struct artifact *weap;
400 
401     if ((weap = get_artifact(otmp)) != 0)
402         return (boolean) (weap->defn.adtyp == adtyp);
403     return FALSE;
404 }
405 
406 /* used for monsters */
407 boolean
defends_when_carried(int adtyp,struct obj * otmp)408 defends_when_carried(int adtyp, struct obj *otmp)
409 {
410     register const struct artifact *weap;
411 
412     if ((weap = get_artifact(otmp)) != 0)
413         return (boolean) (weap->cary.adtyp == adtyp);
414     return FALSE;
415 }
416 
417 /* determine whether an item confers Protection */
418 boolean
protects(struct obj * otmp,boolean being_worn)419 protects(struct obj *otmp, boolean being_worn)
420 {
421     const struct artifact *arti;
422 
423     if (being_worn && objects[otmp->otyp].oc_oprop == PROTECTION)
424         return TRUE;
425     arti = get_artifact(otmp);
426     if (!arti)
427         return FALSE;
428     return (boolean) ((arti->cspfx & SPFX_PROTECT) != 0
429                       || (being_worn && (arti->spfx & SPFX_PROTECT) != 0));
430 }
431 
432 /*
433  * a potential artifact has just been worn/wielded/picked-up or
434  * unworn/unwielded/dropped.  Pickup/drop only set/reset the W_ART mask.
435  */
436 void
set_artifact_intrinsic(struct obj * otmp,boolean on,long wp_mask)437 set_artifact_intrinsic(struct obj *otmp, boolean on, long wp_mask)
438 {
439     long *mask = 0;
440     register const struct artifact *art, *oart = get_artifact(otmp);
441     register struct obj *obj;
442     register uchar dtyp;
443     register long spfx;
444 
445     if (!oart)
446         return;
447 
448     /* effects from the defn field */
449     dtyp = (wp_mask != W_ART) ? oart->defn.adtyp : oart->cary.adtyp;
450 
451     if (dtyp == AD_FIRE)
452         mask = &EFire_resistance;
453     else if (dtyp == AD_COLD)
454         mask = &ECold_resistance;
455     else if (dtyp == AD_ELEC)
456         mask = &EShock_resistance;
457     else if (dtyp == AD_MAGM)
458         mask = &EAntimagic;
459     else if (dtyp == AD_DISN)
460         mask = &EDisint_resistance;
461     else if (dtyp == AD_DRST)
462         mask = &EPoison_resistance;
463     else if (dtyp == AD_DRLI)
464         mask = &EDrain_resistance;
465 
466     if (mask && wp_mask == W_ART && !on) {
467         /* find out if some other artifact also confers this intrinsic;
468            if so, leave the mask alone */
469         for (obj = g.invent; obj; obj = obj->nobj) {
470             if (obj != otmp && obj->oartifact) {
471                 art = get_artifact(obj);
472                 if (art && art->cary.adtyp == dtyp) {
473                     mask = (long *) 0;
474                     break;
475                 }
476             }
477         }
478     }
479     if (mask) {
480         if (on)
481             *mask |= wp_mask;
482         else
483             *mask &= ~wp_mask;
484     }
485 
486     /* intrinsics from the spfx field; there could be more than one */
487     spfx = (wp_mask != W_ART) ? oart->spfx : oart->cspfx;
488     if (spfx && wp_mask == W_ART && !on) {
489         /* don't change any spfx also conferred by other artifacts */
490         for (obj = g.invent; obj; obj = obj->nobj)
491             if (obj != otmp && obj->oartifact) {
492                 art = get_artifact(obj);
493                 if (art)
494                     spfx &= ~art->cspfx;
495             }
496     }
497 
498     if (spfx & SPFX_SEARCH) {
499         if (on)
500             ESearching |= wp_mask;
501         else
502             ESearching &= ~wp_mask;
503     }
504     if (spfx & SPFX_HALRES) {
505         /* make_hallucinated must (re)set the mask itself to get
506          * the display right */
507         /* restoring needed because this is the only artifact intrinsic
508          * that can print a message--need to guard against being printed
509          * when restoring a game
510          */
511         if (u.uroleplay.hallu && on) {
512             u.uroleplay.hallu = FALSE;
513             pline_The("world no longer makes any sense to you!");
514         }
515         (void) make_hallucinated((long) !on,
516                                  g.program_state.restoring ? FALSE : TRUE,
517                                  wp_mask);
518     }
519     if (spfx & SPFX_ESP) {
520         if (on)
521             ETelepat |= wp_mask;
522         else
523             ETelepat &= ~wp_mask;
524         see_monsters();
525     }
526     if (spfx & SPFX_STLTH) {
527         if (on)
528             EStealth |= wp_mask;
529         else
530             EStealth &= ~wp_mask;
531     }
532     if (spfx & SPFX_REGEN) {
533         if (on)
534             ERegeneration |= wp_mask;
535         else
536             ERegeneration &= ~wp_mask;
537     }
538     if (spfx & SPFX_TCTRL) {
539         if (on)
540             ETeleport_control |= wp_mask;
541         else
542             ETeleport_control &= ~wp_mask;
543     }
544     if (spfx & SPFX_WARN) {
545         if (spec_m2(otmp)) {
546             unsigned long type = spec_m2(otmp);
547             boolean is_mlet = !!(spfx & SPFX_DCLAS);
548             /* NOTE: the way warn_of_mon works for warning of monster letters is
549              * currently not particularly robust, and relies on the assumption
550              * that it is impossible to have warning against two different
551              * monster letters from two different artifacts at the same time.
552              * This currently works because the only artifacts that warn of a
553              * monster class (rather than an M2 flag, which should work fine for
554              * multiples) are weapons. */
555             if (on) {
556                 EWarn_of_mon |= wp_mask;
557                 if (is_mlet) {
558                     g.context.warntype.obj_mlet = type;
559                 }
560                 else {
561                     g.context.warntype.obj |= type;
562                 }
563             } else {
564                 EWarn_of_mon &= ~wp_mask;
565                 if (is_mlet) {
566                     g.context.warntype.obj_mlet = 0;
567                 }
568                 else {
569                     g.context.warntype.obj &= ~type;
570                 }
571             }
572             see_monsters();
573         } else {
574             if (on)
575                 EWarning |= wp_mask;
576             else
577                 EWarning &= ~wp_mask;
578         }
579     }
580     if (spfx & SPFX_EREGEN) {
581         if (on)
582             EEnergy_regeneration |= wp_mask;
583         else
584             EEnergy_regeneration &= ~wp_mask;
585     }
586     if (spfx & SPFX_HSPDAM) {
587         if (on)
588             EHalf_spell_damage |= wp_mask;
589         else
590             EHalf_spell_damage &= ~wp_mask;
591     }
592     if (spfx & SPFX_HPHDAM) {
593         if (on)
594             EHalf_physical_damage |= wp_mask;
595         else
596             EHalf_physical_damage &= ~wp_mask;
597     }
598     if (spfx & SPFX_XRAY) {
599         /* this assumes that no one else is using xray_range */
600         if (on)
601             u.xray_range = 3;
602         else
603             u.xray_range = -1;
604         g.vision_full_recalc = 1;
605     }
606     if ((spfx & SPFX_REFLECT) && (wp_mask & W_WEP)) {
607         if (on)
608             EReflecting |= wp_mask;
609         else
610             EReflecting &= ~wp_mask;
611     }
612     if (spfx & SPFX_PROTECT) {
613         if (on)
614             EProtection |= wp_mask;
615         else
616             EProtection &= ~wp_mask;
617     }
618 
619     if (wp_mask == W_ART && !on && oart->inv_prop) {
620         /* might have to turn off invoked power too */
621         if (oart->inv_prop <= LAST_PROP
622             && (u.uprops[oart->inv_prop].extrinsic & W_ARTI))
623             (void) arti_invoke(otmp);
624     }
625 }
626 
627 /* touch_artifact()'s return value isn't sufficient to tell whether it
628    dished out damage, and tracking changes to u.uhp, u.mh, Lifesaved
629    when trying to avoid second wounding is too cumbersome */
630 static boolean touch_blasted; /* for retouch_object() */
631 
632 /*
633  * creature (usually hero) tries to touch (pick up or wield) an artifact obj.
634  * Returns 0 if the object refuses to be touched.
635  * This routine does not change any object chains.
636  * Ignores such things as gauntlets, assuming the artifact is not
637  * fooled by such trappings.
638  */
639 int
touch_artifact(struct obj * obj,struct monst * mon)640 touch_artifact(struct obj *obj, struct monst *mon)
641 {
642     register const struct artifact *oart = get_artifact(obj);
643     boolean badclass, badalign, self_willed, yours;
644 
645     touch_blasted = FALSE;
646     if (!oart)
647         return 1;
648 
649     yours = (mon == &g.youmonst);
650     /* all quest artifacts are self-willed; if this ever changes, `badclass'
651        will have to be extended to explicitly include quest artifacts */
652     self_willed = ((oart->spfx & SPFX_INTEL) != 0);
653     if (yours) {
654         u.uconduct.artitouch++;
655         badclass = self_willed
656                    && ((oart->role != NON_PM && !Role_if(oart->role))
657                        || (oart->race != NON_PM && !Race_if(oart->race)));
658         badalign = ((oart->spfx & SPFX_RESTR) != 0
659                     && oart->alignment != A_NONE
660                     && (oart->alignment != u.ualign.type
661                         || u.ualign.record < 0));
662     } else if (!is_covetous(mon->data) && !is_mplayer(mon->data)) {
663         badclass = self_willed && oart->role != NON_PM
664                    && oart != &artilist[ART_EXCALIBUR];
665         badalign = (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE
666                    && (oart->alignment != mon_aligntyp(mon));
667     } else { /* an M3_WANTSxxx monster or a fake player */
668         /* special monsters trying to take the Amulet, invocation tools or
669            quest item can touch anything except `spec_applies' artifacts */
670         badclass = badalign = FALSE;
671     }
672     /* weapons which attack specific categories of monsters are
673        bad for them even if their alignments happen to match */
674     if (!badalign)
675         badalign = bane_applies(oart, mon);
676 
677     if (((badclass || badalign) && self_willed)
678         || (badalign && (!yours || !rn2(4)))) {
679         int dmg;
680         char buf[BUFSZ];
681 
682         if (!yours)
683             return 0;
684         You("are blasted by %s power!", s_suffix(the(xname(obj))));
685         touch_blasted = TRUE;
686         dmg = d((Antimagic ? 2 : 4), (self_willed ? 10 : 4));
687         /* add half of the usual material damage bonus */
688         if (Hate_material(obj->material)) {
689             dmg += (rnd(sear_damage(obj->material)) / 2) + 1;
690         }
691         Sprintf(buf, "touching %s", oart->name);
692         losehp(dmg, buf, KILLED_BY); /* magic damage, not physical */
693         exercise(A_WIS, FALSE);
694     }
695 
696     /* can pick it up unless you're totally non-synch'd with the artifact */
697     if (badclass && badalign && self_willed) {
698         if (yours) {
699             if (!carried(obj))
700                 pline("%s your grasp!", Tobjnam(obj, "evade"));
701             else
702                 pline("%s beyond your control!", Tobjnam(obj, "are"));
703         }
704         return 0;
705     }
706 
707     return 1;
708 }
709 
710 /* decide whether an artifact itself is vulnerable to a particular type
711    of erosion damage, independent of the properties of its bearer */
712 boolean
arti_immune(struct obj * obj,int dtyp)713 arti_immune(struct obj *obj, int dtyp)
714 {
715     register const struct artifact *weap = get_artifact(obj);
716 
717     if (!weap)
718         return FALSE;
719     if (dtyp == AD_PHYS)
720         return FALSE; /* nothing is immune to phys dmg */
721     return (boolean) (weap->attk.adtyp == dtyp
722                       || weap->defn.adtyp == dtyp
723                       || weap->cary.adtyp == dtyp);
724 }
725 
726 static boolean
bane_applies(const struct artifact * oart,struct monst * mon)727 bane_applies(const struct artifact *oart, struct monst *mon)
728 {
729     struct artifact atmp;
730 
731     if (oart && (oart->spfx & SPFX_DBONUS) != 0) {
732         atmp = *oart;
733         atmp.spfx &= SPFX_DBONUS; /* clear other spfx fields */
734         if (spec_applies(&atmp, mon))
735             return TRUE;
736     }
737     return FALSE;
738 }
739 
740 /* decide whether an artifact's special attacks apply against mtmp */
741 static int
spec_applies(const struct artifact * weap,struct monst * mtmp)742 spec_applies(const struct artifact *weap, struct monst *mtmp)
743 {
744     struct permonst *ptr;
745     boolean yours;
746 
747     if (!(weap->spfx & (SPFX_DBONUS | SPFX_ATTK)))
748         return (weap->attk.adtyp == AD_PHYS);
749 
750     yours = (mtmp == &g.youmonst);
751     ptr = mtmp->data;
752 
753     if (weap->spfx & SPFX_DMONS) {
754         return (ptr == &mons[(int) weap->mtype]);
755     } else if (weap->spfx & SPFX_DCLAS) {
756         return (weap->mtype == (unsigned long) ptr->mlet);
757     } else if (weap->spfx & SPFX_DFLAG1) {
758         return ((ptr->mflags1 & weap->mtype) != 0L);
759     } else if (weap->spfx & SPFX_DFLAG2) {
760         return ((ptr->mflags2 & weap->mtype)
761                 || (yours
762                     && ((!Upolyd && (g.urace.selfmask & weap->mtype))
763                         || ((weap->mtype & M2_WERE) && u.ulycn >= LOW_PM))));
764     } else if (weap->spfx & SPFX_DALIGN) {
765         return yours ? (u.ualign.type != weap->alignment)
766                      : (ptr->maligntyp == A_NONE
767                         || sgn(ptr->maligntyp) != weap->alignment);
768     } else if (weap->spfx & SPFX_ATTK) {
769         struct obj *defending_weapon = (yours ? uwep : MON_WEP(mtmp));
770 
771         if (defending_weapon && defending_weapon->oartifact
772             && defends((int) weap->attk.adtyp, defending_weapon))
773             return FALSE;
774         switch (weap->attk.adtyp) {
775         case AD_FIRE:
776             return !(yours ? Fire_resistance : resists_fire(mtmp));
777         case AD_COLD:
778             return !(yours ? Cold_resistance : resists_cold(mtmp));
779         case AD_ELEC:
780             return !(yours ? Shock_resistance : resists_elec(mtmp));
781         case AD_MAGM:
782         case AD_STUN:
783             return !(yours ? Antimagic : (rn2(100) < ptr->mr));
784         case AD_DRST:
785             return !(yours ? Poison_resistance : resists_poison(mtmp));
786         case AD_DRLI:
787             return !(yours ? Drain_resistance : resists_drli(mtmp));
788         case AD_STON:
789             return !(yours ? Stone_resistance : resists_ston(mtmp));
790         default:
791             impossible("Weird weapon special attack.");
792         }
793     }
794     return 0;
795 }
796 
797 /* return the M2 flags of monster that an artifact's special attacks apply
798  * against */
799 long
spec_m2(struct obj * otmp)800 spec_m2(struct obj *otmp)
801 {
802     const struct artifact *artifact = get_artifact(otmp);
803 
804     if (artifact)
805         return artifact->mtype;
806     return 0L;
807 }
808 
809 /* special attack bonus */
810 int
spec_abon(struct obj * otmp,struct monst * mon)811 spec_abon(struct obj *otmp, struct monst *mon)
812 {
813     const struct artifact *weap = get_artifact(otmp);
814 
815     /* no need for an extra check for `NO_ATTK' because this will
816        always return 0 for any artifact which has that attribute */
817 
818     if (weap && weap->attk.damn && spec_applies(weap, mon))
819         return rnd((int) weap->attk.damn);
820     return 0;
821 }
822 
823 /* special damage bonus */
824 int
spec_dbon(struct obj * otmp,struct monst * mon,int tmp)825 spec_dbon(struct obj *otmp, struct monst *mon, int tmp)
826 {
827     register const struct artifact *weap = get_artifact(otmp);
828 
829     if (!weap || (weap->attk.adtyp == AD_PHYS /* check for `NO_ATTK' */
830                   && weap->attk.damn == 0 && weap->attk.damd == 0))
831         g.spec_dbon_applies = FALSE;
832     else if (otmp->oartifact == ART_GRIMTOOTH)
833         /* Grimtooth has SPFX settings to warn against elves but we want its
834            damage bonus to apply to all targets, so bypass spec_applies() */
835         g.spec_dbon_applies = TRUE;
836     else
837         g.spec_dbon_applies = spec_applies(weap, mon);
838 
839     if (g.spec_dbon_applies)
840         return weap->attk.damd ? rnd((int) weap->attk.damd) : max(tmp, 1);
841     return 0;
842 }
843 
844 /* add identified artifact to discoveries list */
845 void
discover_artifact(xchar m)846 discover_artifact(xchar m)
847 {
848     int i;
849 
850     /* look for this artifact in the discoveries list;
851        if we hit an empty slot then it's not present, so add it */
852     for (i = 0; i < NROFARTIFACTS; i++)
853         if (g.artidisco[i] == 0 || g.artidisco[i] == m) {
854             g.artidisco[i] = m;
855             return;
856         }
857     /* there is one slot per artifact, so we should never reach the
858        end without either finding the artifact or an empty slot... */
859     impossible("couldn't discover artifact (%d)", (int) m);
860 }
861 
862 /* used to decide whether an artifact has been fully identified */
863 boolean
undiscovered_artifact(xchar m)864 undiscovered_artifact(xchar m)
865 {
866     int i;
867 
868     /* look for this artifact in the discoveries list;
869        if we hit an empty slot then it's undiscovered */
870     for (i = 0; i < NROFARTIFACTS; i++)
871         if (g.artidisco[i] == m)
872             return FALSE;
873         else if (g.artidisco[i] == 0)
874             break;
875     return TRUE;
876 }
877 
878 /* display a list of discovered artifacts; return their count */
879 int
disp_artifact_discoveries(winid tmpwin)880 disp_artifact_discoveries(winid tmpwin) /* supplied by dodiscover() */
881 {
882     int i, m, otyp;
883     char buf[BUFSZ];
884 
885     for (i = 0; i < NROFARTIFACTS; i++) {
886         if (g.artidisco[i] == 0)
887             break; /* empty slot implies end of list */
888         if (tmpwin == WIN_ERR)
889             continue; /* for WIN_ERR, we just count */
890 
891         if (i == 0)
892             putstr(tmpwin, iflags.menu_headings, "Artifacts");
893         m = g.artidisco[i];
894         otyp = artilist[m].otyp;
895         Sprintf(buf, "  %s [%s %s]", artiname(m),
896                 align_str(artilist[m].alignment), simple_typename(otyp));
897         putstr(tmpwin, 0, buf);
898     }
899     return i;
900 }
901 
902 /*
903  * Magicbane's intrinsic magic is incompatible with normal
904  * enchantment magic.  Thus, its effects have a negative
905  * dependence on spe.  Against low mr victims, it typically
906  * does "double athame" damage, 2d4.  Occasionally, it will
907  * cast unbalancing magic which effectively averages out to
908  * 4d4 damage (3d4 against high mr victims), for spe = 0.
909  *
910  * Prior to 3.4.1, the cancel (aka purge) effect always
911  * included the scare effect too; now it's one or the other.
912  * Likewise, the stun effect won't be combined with either
913  * of those two; it will be chosen separately or possibly
914  * used as a fallback when scare or cancel fails.
915  *
916  * [Historical note: a change to artifact_hit() for 3.4.0
917  * unintentionally made all of Magicbane's special effects
918  * be blocked if the defender successfully saved against a
919  * stun attack.  As of 3.4.1, those effects can occur but
920  * will be slightly less likely than they were in 3.3.x.]
921  */
922 
923 enum mb_effect_indices {
924     MB_INDEX_PROBE = 0,
925     MB_INDEX_STUN,
926     MB_INDEX_SCARE,
927     MB_INDEX_CANCEL,
928 
929     NUM_MB_INDICES
930 };
931 
932 #define MB_MAX_DIEROLL 8 /* rolls above this aren't magical */
933 static const char *const mb_verb[2][NUM_MB_INDICES] = {
934     { "probe", "stun", "scare", "cancel" },
935     { "prod", "amaze", "tickle", "purge" },
936 };
937 
938 /* called when someone is being hit by Magicbane */
939 static boolean
Mb_hit(struct monst * magr,struct monst * mdef,struct obj * mb,int * dmgptr,int dieroll,boolean vis,char * hittee)940 Mb_hit(struct monst *magr, /* attacker */
941        struct monst *mdef, /* defender */
942        struct obj *mb,     /* Magicbane */
943        int *dmgptr,        /* extra damage target will suffer */
944        int dieroll,        /* d20 that has already scored a hit */
945        boolean vis,        /* whether the action can be seen */
946        char *hittee)       /* target's name: "you" or mon_nam(mdef) */
947 {
948     struct permonst *old_uasmon;
949     const char *verb;
950     boolean youattack = (magr == &g.youmonst), youdefend = (mdef == &g.youmonst),
951             resisted = FALSE, do_stun, do_confuse, result;
952     int attack_indx, fakeidx, scare_dieroll = MB_MAX_DIEROLL / 2;
953 
954     result = FALSE; /* no message given yet */
955     /* the most severe effects are less likely at higher enchantment */
956     if (mb->spe >= 3)
957         scare_dieroll /= (1 << (mb->spe / 3));
958     /* if target successfully resisted the artifact damage bonus,
959        reduce overall likelihood of the assorted special effects */
960     if (!g.spec_dbon_applies)
961         dieroll += 1;
962 
963     /* might stun even when attempting a more severe effect, but
964        in that case it will only happen if the other effect fails;
965        extra damage will apply regardless; 3.4.1: sometimes might
966        just probe even when it hasn't been enchanted */
967     do_stun = (max(mb->spe, 0) < rn2(g.spec_dbon_applies ? 11 : 7));
968 
969     /* the special effects also boost physical damage; increments are
970        generally cumulative, but since the stun effect is based on a
971        different criterium its damage might not be included; the base
972        damage is either 1d4 (athame) or 2d4 (athame+spec_dbon) depending
973        on target's resistance check against AD_STUN (handled by caller)
974        [note that a successful save against AD_STUN doesn't actually
975        prevent the target from ending up stunned] */
976     attack_indx = MB_INDEX_PROBE;
977     *dmgptr += rnd(4); /* (2..3)d4 */
978     if (do_stun) {
979         attack_indx = MB_INDEX_STUN;
980         *dmgptr += rnd(4); /* (3..4)d4 */
981     }
982     if (dieroll <= scare_dieroll) {
983         attack_indx = MB_INDEX_SCARE;
984         *dmgptr += rnd(4); /* (3..5)d4 */
985     }
986     if (dieroll <= (scare_dieroll / 2)) {
987         attack_indx = MB_INDEX_CANCEL;
988         *dmgptr += rnd(4); /* (4..6)d4 */
989     }
990 
991     /* give the hit message prior to inflicting the effects */
992     verb = mb_verb[!!Hallucination][attack_indx];
993     if (youattack || youdefend || vis) {
994         result = TRUE;
995         pline_The("magic-absorbing blade %s %s!",
996                   vtense((const char *) 0, verb), hittee);
997         /* assume probing has some sort of noticeable feedback
998            even if it is being done by one monster to another */
999         if (attack_indx == MB_INDEX_PROBE && !canspotmon(mdef))
1000             map_invisible(mdef->mx, mdef->my);
1001     }
1002 
1003     /* now perform special effects */
1004     switch (attack_indx) {
1005     case MB_INDEX_CANCEL:
1006         old_uasmon = g.youmonst.data;
1007         /* No mdef->mcan check: even a cancelled monster can be polymorphed
1008          * into a golem, and the "cancel" effect acts as if some magical
1009          * energy remains in spellcasting defenders to be absorbed later.
1010          */
1011         if (!cancel_monst(mdef, mb, youattack, FALSE, FALSE)) {
1012             resisted = TRUE;
1013         } else {
1014             do_stun = FALSE;
1015             if (youdefend) {
1016                 if (g.youmonst.data != old_uasmon)
1017                     *dmgptr = 0; /* rehumanized, so no more damage */
1018                 if (u.uenmax > 0) {
1019                     u.uenmax--;
1020                     if (u.uen > 0)
1021                         u.uen--;
1022                     g.context.botl = TRUE;
1023                     You("lose magical energy!");
1024                 }
1025             } else {
1026                 if (mdef->data == &mons[PM_CLAY_GOLEM])
1027                     mdef->mhp = 1; /* cancelled clay golems will die */
1028                 if (youattack && attacktype(mdef->data, AT_MAGC)) {
1029                     u.uenmax++;
1030                     u.uen++;
1031                     g.context.botl = TRUE;
1032                     You("absorb magical energy!");
1033                 }
1034             }
1035         }
1036         break;
1037 
1038     case MB_INDEX_SCARE:
1039         if (youdefend) {
1040             if (Antimagic) {
1041                 resisted = TRUE;
1042             } else {
1043                 nomul(-3);
1044                 g.multi_reason = "being scared stiff";
1045                 g.nomovemsg = "";
1046                 if (magr && magr == u.ustuck && sticks(g.youmonst.data)) {
1047                     set_ustuck((struct monst *) 0);
1048                     You("release %s!", mon_nam(magr));
1049                 }
1050             }
1051         } else {
1052             if (rn2(2) && resist(mdef, WEAPON_CLASS, 0, NOTELL))
1053                 resisted = TRUE;
1054             else
1055                 monflee(mdef, 3, FALSE, (mdef->mhp > *dmgptr));
1056         }
1057         if (!resisted)
1058             do_stun = FALSE;
1059         break;
1060 
1061     case MB_INDEX_STUN:
1062         do_stun = TRUE; /* (this is redundant...) */
1063         break;
1064 
1065     case MB_INDEX_PROBE:
1066         if (youattack && (mb->spe == 0 || !rn2(3 * abs(mb->spe)))) {
1067             pline_The("%s is insightful.", verb);
1068             /* pre-damage status */
1069             probe_monster(mdef);
1070         }
1071         break;
1072     }
1073     /* stun if that was selected and a worse effect didn't occur */
1074     if (do_stun) {
1075         if (youdefend)
1076             make_stunned(((HStun & TIMEOUT) + 3L), FALSE);
1077         else
1078             mdef->mstun = 1;
1079         /* avoid extra stun message below if we used mb_verb["stun"] above */
1080         if (attack_indx == MB_INDEX_STUN)
1081             do_stun = FALSE;
1082     }
1083     /* lastly, all this magic can be confusing... */
1084     do_confuse = !rn2(12);
1085     if (do_confuse) {
1086         if (youdefend)
1087             make_confused((HConfusion & TIMEOUT) + 4L, FALSE);
1088         else
1089             mdef->mconf = 1;
1090     }
1091 
1092     /* now give message(s) describing side-effects; Use fakename
1093        so vtense() won't be fooled by assigned name ending in 's' */
1094     fakeidx = youdefend ? 1 : 0;
1095     if (youattack || youdefend || vis) {
1096         (void) upstart(hittee); /* capitalize */
1097         if (resisted) {
1098             pline("%s %s!", hittee, vtense(fakename[fakeidx], "resist"));
1099             shieldeff(youdefend ? u.ux : mdef->mx,
1100                       youdefend ? u.uy : mdef->my);
1101         }
1102         if ((do_stun || do_confuse) && flags.verbose) {
1103             char buf[BUFSZ];
1104 
1105             buf[0] = '\0';
1106             if (do_stun)
1107                 Strcat(buf, "stunned");
1108             if (do_stun && do_confuse)
1109                 Strcat(buf, " and ");
1110             if (do_confuse)
1111                 Strcat(buf, "confused");
1112             pline("%s %s %s%c", hittee, vtense(fakename[fakeidx], "are"), buf,
1113                   (do_stun && do_confuse) ? '!' : '.');
1114         }
1115     }
1116 
1117     return result;
1118 }
1119 
1120 /* Function used when someone attacks someone else with an artifact
1121  * weapon.  Only adds the special (artifact) damage, and returns a bitmask of
1122  * the ARTIFACTHIT flags defined in hack.h indicating what messages it
1123  * printed, so that the caller can avoid printing messages as needed.
1124  * This should be called once upon every artifact attack; dmgval() no longer
1125  * takes artifact bonuses into account.  Possible extension: change the killer
1126  * so that when an orc kills you with Stormbringer it's "killed by Stormbringer"
1127  * instead of "killed by an orc".
1128  */
1129 int
artifact_hit(struct monst * magr,struct monst * mdef,struct obj * otmp,int * dmgptr,int dieroll)1130 artifact_hit(struct monst *magr, struct monst *mdef, struct obj *otmp,
1131              int *dmgptr,
1132              int dieroll) /* needed for Magicbane and vorpal blades */
1133 {
1134     boolean youattack = (magr == &g.youmonst);
1135     boolean youdefend = (mdef == &g.youmonst);
1136     boolean vis = (!youattack && magr && cansee(magr->mx, magr->my))
1137                   || (!youdefend && cansee(mdef->mx, mdef->my))
1138                   || (youattack && u.uswallow && mdef == u.ustuck && !Blind);
1139     int retval = ARTIFACTHIT_NOMSG;
1140     boolean realizes_damage;
1141     const char *wepdesc;
1142     static const char you[] = "you";
1143     char hittee[BUFSZ];
1144 
1145     Strcpy(hittee, youdefend ? you : mon_nam(mdef));
1146 
1147     /* The following takes care of most of the damage, but not all--
1148      * the exception being for level draining, which is specially
1149      * handled.  Messages are done in this function, however.
1150      */
1151     *dmgptr += spec_dbon(otmp, mdef, *dmgptr);
1152 
1153     if (youattack && youdefend) {
1154         impossible("attacking yourself with weapon?");
1155         return ARTIFACTHIT_NOMSG;
1156     }
1157 
1158     realizes_damage = (youdefend || vis
1159                        /* feel the effect even if not seen */
1160                        || (youattack && mdef == u.ustuck));
1161 
1162     /* the four basic attacks: fire, cold, shock and missiles */
1163     if (attacks(AD_FIRE, otmp)) {
1164         if (realizes_damage) {
1165             pline_The("fiery blade %s %s%c",
1166                       !g.spec_dbon_applies
1167                           ? "hits"
1168                           : (mdef->data == &mons[PM_WATER_ELEMENTAL]
1169                              || mdef->data == &mons[PM_ICE_VORTEX])
1170                                 ? "vaporizes part of"
1171                                 : "burns",
1172                       hittee, !g.spec_dbon_applies ? '.' : '!');
1173             retval |= ARTIFACTHIT_GAVEMSG;
1174             if (completelyburns(mdef->data) || is_wooden(mdef->data)
1175                 || mdef->data == &mons[PM_GREEN_SLIME]) {
1176                 if (youdefend) {
1177                     You("ignite and turn to ash!");
1178                     losehp((Upolyd ? u.mh : u.uhp) + 1, "immolation",
1179                            NO_KILLER_PREFIX);
1180                 }
1181                 else {
1182                     pline("%s ignites and turns to ash!", Monnam(mdef));
1183                     *dmgptr = mdef->mhp + FATAL_DAMAGE_MODIFIER;
1184                     mdef->golem_destroyed = 1;
1185                 }
1186                 retval |= ARTIFACTHIT_INSTAKILLMSG;
1187             }
1188         }
1189         if (!rn2(4)) {
1190             int itemdmg = destroy_items(mdef, AD_FIRE, *dmgptr);
1191             if (!youdefend)
1192                 /* kludge for destroy_items only dealing damage if it's the
1193                  * player */
1194                 *dmgptr += itemdmg;
1195             ignite_items(mdef->minvent);
1196         }
1197         if (youdefend && Slimed)
1198             burn_away_slime();
1199         return retval;
1200     }
1201     if (attacks(AD_COLD, otmp)) {
1202         if (realizes_damage) {
1203             pline_The("ice-cold blade %s %s%c",
1204                       !g.spec_dbon_applies ? "hits" : "freezes", hittee,
1205                       !g.spec_dbon_applies ? '.' : '!');
1206             retval |= ARTIFACTHIT_GAVEMSG;
1207         }
1208         if (!rn2(4)) {
1209             int itemdmg = destroy_items(mdef, AD_COLD, *dmgptr);
1210             if (!youdefend)
1211                 *dmgptr += itemdmg; /* same kludge as above */
1212         }
1213         return retval;
1214     }
1215     if (attacks(AD_ELEC, otmp)) {
1216         if (realizes_damage) {
1217             pline_The("massive hammer hits%s %s%c",
1218                       !g.spec_dbon_applies ? "" : "!  Lightning strikes",
1219                       hittee, !g.spec_dbon_applies ? '.' : '!');
1220             retval |= ARTIFACTHIT_GAVEMSG;
1221         }
1222         if (g.spec_dbon_applies)
1223             wake_nearto(mdef->mx, mdef->my, 4 * 4);
1224         if (!rn2(5)) {
1225             int itemdmg = destroy_items(mdef, AD_ELEC, *dmgptr);
1226             if (!youdefend)
1227                 *dmgptr += itemdmg;
1228         }
1229         wake_nearto(mdef->mx, mdef->my, 6 * 6); /* thunder */
1230         return retval;
1231     }
1232     if (attacks(AD_MAGM, otmp)) {
1233         if (realizes_damage) {
1234             pline_The("imaginary widget hits%s %s%c",
1235                       !g.spec_dbon_applies
1236                           ? ""
1237                           : "!  A hail of magic missiles strikes",
1238                       hittee, !g.spec_dbon_applies ? '.' : '!');
1239             retval |= ARTIFACTHIT_GAVEMSG;
1240         }
1241         return retval;
1242     }
1243 
1244     if (attacks(AD_STUN, otmp) && dieroll <= MB_MAX_DIEROLL) {
1245         /* Magicbane's special attacks (possibly modifies hittee[]) */
1246         if (Mb_hit(magr, mdef, otmp, dmgptr, dieroll, vis, hittee)) {
1247             return ARTIFACTHIT_GAVEMSG;
1248         }
1249         else {
1250             return ARTIFACTHIT_NOMSG;
1251         }
1252     }
1253 
1254     /* poison damage is a 1/4 chance; use dieroll to make sure the message syncs
1255      * up with the poison effects */
1256     if (permapoisoned(otmp) && realizes_damage && dieroll <= 5
1257         && !(youdefend ? Poison_resistance : resists_poison(mdef))) {
1258         pline_The("jagged blade poisons %s!", hittee);
1259         return ARTIFACTHIT_GAVEMSG;
1260     }
1261 
1262     if (!g.spec_dbon_applies) {
1263         /* since damage bonus didn't apply, nothing more to do;
1264            no further attacks have side-effects on inventory */
1265         return ARTIFACTHIT_NOMSG;
1266     }
1267 
1268     /* We really want "on a natural 20" but Nethack does it in */
1269     /* reverse from AD&D. */
1270     if (spec_ability(otmp, SPFX_BEHEAD)) {
1271         if (otmp->oartifact == ART_TSURUGI_OF_MURAMASA && dieroll == 1) {
1272             wepdesc = "The razor-sharp blade";
1273             /* not really beheading, but so close, why add another SPFX */
1274             if (youattack && u.uswallow && mdef == u.ustuck) {
1275                 You("slice %s wide open!", mon_nam(mdef));
1276                 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1277                 return ARTIFACTHIT_INSTAKILLMSG | ARTIFACTHIT_GAVEMSG;
1278             }
1279             if (!youdefend) {
1280                 /* allow normal cutworm() call to add extra damage */
1281                 if (g.notonhead)
1282                     return ARTIFACTHIT_NOMSG;
1283 
1284                 if (bigmonst(mdef->data)) {
1285                     if (youattack)
1286                         You("slice deeply into %s!", mon_nam(mdef));
1287                     else if (vis)
1288                         pline("%s cuts deeply into %s!", Monnam(magr),
1289                               hittee);
1290                     *dmgptr *= 2;
1291                     return ARTIFACTHIT_GAVEMSG;
1292                 }
1293                 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1294                 pline("%s cuts %s in half!", wepdesc, mon_nam(mdef));
1295                 otmp->dknown = TRUE;
1296                 return ARTIFACTHIT_INSTAKILLMSG | ARTIFACTHIT_GAVEMSG;
1297             } else {
1298                 if (bigmonst(g.youmonst.data)) {
1299                     pline("%s cuts deeply into you!",
1300                           magr ? Monnam(magr) : wepdesc);
1301                     *dmgptr *= 2;
1302                     return ARTIFACTHIT_GAVEMSG;
1303                 }
1304 
1305                 /* Players with negative AC's take less damage instead
1306                  * of just not getting hit.  We must add a large enough
1307                  * value to the damage so that this reduction in
1308                  * damage does not prevent death.
1309                  */
1310                 *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
1311                 pline("%s cuts you in half!", wepdesc);
1312                 otmp->dknown = TRUE;
1313                 return ARTIFACTHIT_INSTAKILLMSG | ARTIFACTHIT_GAVEMSG;
1314             }
1315         } else if (otmp->oartifact == ART_VORPAL_BLADE
1316                    && (dieroll == 1 || mdef->data == &mons[PM_JABBERWOCK])) {
1317             static const char *const behead_msg[2] = { "%s beheads %s!",
1318                                                        "%s decapitates %s!" };
1319 
1320             if (youattack && u.uswallow && mdef == u.ustuck)
1321                 return ARTIFACTHIT_NOMSG;
1322             wepdesc = artilist[ART_VORPAL_BLADE].name;
1323             if (!youdefend) {
1324                 if (!has_head(mdef->data) || g.notonhead || u.uswallow) {
1325                     if (youattack) {
1326                         pline("Somehow, you miss %s wildly.", mon_nam(mdef));
1327                         retval |= ARTIFACTHIT_GAVEMSG;
1328                     }
1329                     else if (vis) {
1330                         pline("Somehow, %s misses wildly.", mon_nam(magr));
1331                         retval |= ARTIFACTHIT_GAVEMSG;
1332                     }
1333                     *dmgptr = 0;
1334                     return retval;
1335                 }
1336                 if (noncorporeal(mdef->data) || amorphous(mdef->data)) {
1337                     pline("%s slices through %s %s.", wepdesc,
1338                           s_suffix(mon_nam(mdef)), mbodypart(mdef, NECK));
1339                     return ARTIFACTHIT_GAVEMSG;
1340                 }
1341                 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1342                 pline(behead_msg[rn2(SIZE(behead_msg))], wepdesc,
1343                       mon_nam(mdef));
1344                 if (Hallucination && !flags.female)
1345                     pline("Good job Henry, but that wasn't Anne.");
1346                 if (is_reviver(mdef->data) && !is_rider(mdef->data)
1347                     && (!mlifesaver(mdef)
1348                         || faulty_lifesaver(mlifesaver(mdef)))) {
1349                     /* kind of hard to revive if you've lost your head...
1350                      * if they'll be lifesaved, however, we shouldn't cancel
1351                      * them, because we assume that'll fix them. */
1352                     mdef->mcan = 1;
1353                 }
1354                 otmp->dknown = TRUE;
1355                 return ARTIFACTHIT_INSTAKILLMSG | ARTIFACTHIT_GAVEMSG;
1356             } else {
1357                 if (!has_head(g.youmonst.data)) {
1358                     pline("Somehow, %s misses you wildly.",
1359                           magr ? mon_nam(magr) : wepdesc);
1360                     *dmgptr = 0;
1361                     return ARTIFACTHIT_GAVEMSG;
1362                 }
1363                 if (noncorporeal(g.youmonst.data)
1364                     || amorphous(g.youmonst.data)) {
1365                     pline("%s slices through your %s.", wepdesc,
1366                           body_part(NECK));
1367                     return ARTIFACTHIT_GAVEMSG;
1368                 }
1369                 *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
1370                 pline(behead_msg[rn2(SIZE(behead_msg))], wepdesc, "you");
1371                 otmp->dknown = TRUE;
1372                 /* Should amulets fall off? */
1373                 return ARTIFACTHIT_INSTAKILLMSG | ARTIFACTHIT_GAVEMSG;
1374             }
1375         }
1376     }
1377     if (spec_ability(otmp, SPFX_HEAVYHIT)) {
1378         if (otmp->oartifact == ART_OGRESMASHER && dieroll == 1) {
1379             boolean smallmonst = (mdef->data->msize <= MZ_SMALL);
1380             wepdesc = artilist[ART_OGRESMASHER].name;
1381 
1382             if (youdefend) {
1383                 otmp->dknown = TRUE;
1384                 if (smallmonst) {
1385                     pline("%s crushes you!", wepdesc);
1386                     *dmgptr = 2 * (Upolyd ? u.mh : u.uhp)
1387                               + FATAL_DAMAGE_MODIFIER;
1388                     return ARTIFACTHIT_INSTAKILLMSG | ARTIFACTHIT_GAVEMSG;
1389                 }
1390                 else {
1391                     pline("%s smashes into you!", wepdesc);
1392                     /* No extra damage, but kill speed and inflict status
1393                      * ailments. */
1394                     if (HFast)
1395                         u_slow_down(); /* avoid multiple "You slow down" */
1396                     make_stunned((HStun & TIMEOUT) + (long) rn1(5, 5), TRUE);
1397                     return ARTIFACTHIT_GAVEMSG;
1398                 }
1399             }
1400             else {
1401                 if (smallmonst) {
1402                     if (vis) {
1403                         pline("%s crushes %s!", wepdesc, mon_nam(mdef));
1404                         otmp->dknown = TRUE;
1405                         retval |= ARTIFACTHIT_INSTAKILLMSG | ARTIFACTHIT_GAVEMSG;
1406                     }
1407                     *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1408                 }
1409                 else {
1410                     if (vis) {
1411                         if (youattack)
1412                             You("smash %s into %s!", wepdesc, mon_nam(mdef));
1413                         else
1414                             pline("%s smashes %s into %s!", Monnam(magr),
1415                                   wepdesc, mon_nam(mdef));
1416                         otmp->dknown = TRUE;
1417                         retval |= ARTIFACTHIT_GAVEMSG;
1418                     }
1419                     mon_adjust_speed(mdef, -2, NULL);
1420                     mdef->mstun = 1;
1421                 }
1422                 return retval;
1423             }
1424         }
1425     }
1426     if (spec_ability(otmp, SPFX_DRLI)) {
1427         /* some non-living creatures (golems, vortices) are vulnerable to
1428            life drain effects so can get "<Arti> draws the <life>" feedback */
1429         const char *life = nonliving(mdef->data) ? "animating force" : "life";
1430         if (item_catches_drain(mdef)) {
1431             /* This has to go here rather than along with the resists_drli
1432              * check; otherwise a drainable item gets drained even if the
1433              * attack is a miss.
1434              * Return NOMSG because even though item_catches_drain would have
1435              * printed something, artifact_hit didn't. */
1436             return ARTIFACTHIT_NOMSG;
1437         }
1438 
1439         if (!youdefend) {
1440             int m_lev = (int) mdef->m_lev, /* will be 0 for 1d4 mon */
1441                 mhpmax = mdef->mhpmax,
1442                 drain = monhp_per_lvl(mdef); /* usually 1d8 */
1443                 /* note: DRLI attack uses 2d6, attacker doesn't get healed */
1444 
1445             /* stop draining HP if it drops too low (still drains level;
1446                also caller still inflicts regular weapon damage) */
1447             if (mhpmax - drain <= m_lev)
1448                 drain = (mhpmax > m_lev) ? (mhpmax - (m_lev + 1)) : 0;
1449 
1450             if (vis) {
1451                 if (otmp->oartifact == ART_STORMBRINGER)
1452                     pline_The("%s blade draws the %s from %s!",
1453                               hcolor(NH_BLACK), life, mon_nam(mdef));
1454                 else
1455                     pline("%s draws the %s from %s!",
1456                           The(distant_name(otmp, xname)), life,
1457                           mon_nam(mdef));
1458                 retval |= ARTIFACTHIT_GAVEMSG;
1459             }
1460             if (mdef->m_lev == 0) {
1461                 /* losing a level when at 0 is fatal */
1462                 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1463             } else {
1464                 *dmgptr += drain;
1465                 mdef->mhpmax -= drain;
1466                 mdef->m_lev--;
1467             }
1468 
1469             if (drain > 0) {
1470                 /* drain: was target's damage, now heal attacker by half */
1471                 drain = (drain + 1) / 2; /* drain/2 rounded up */
1472                 if (youattack) {
1473                     healup(drain, 0, FALSE, FALSE);
1474                 } else {
1475                     magr->mhp += drain;
1476                     if (magr->mhp > magr->mhpmax)
1477                         magr->mhp = magr->mhpmax;
1478                 }
1479             }
1480             return retval;
1481         } else { /* youdefend */
1482             int oldhpmax = u.uhpmax;
1483 
1484             if (Blind)
1485                 You_feel("an %s drain your %s!",
1486                          (otmp->oartifact == ART_STORMBRINGER)
1487                             ? "unholy blade"
1488                             : "object",
1489                          life);
1490             else if (otmp->oartifact == ART_STORMBRINGER)
1491                 pline_The("%s blade drains your %s!", hcolor(NH_BLACK), life);
1492             else
1493                 pline("%s drains your %s!", The(distant_name(otmp, xname)),
1494                       life);
1495             losexp("life drainage");
1496             if (magr && magr->mhp < magr->mhpmax) {
1497                 magr->mhp += (oldhpmax - u.uhpmax + 1) / 2;
1498                 if (magr->mhp > magr->mhpmax)
1499                     magr->mhp = magr->mhpmax;
1500             }
1501             return ARTIFACTHIT_GAVEMSG;
1502         }
1503     }
1504     return ARTIFACTHIT_NOMSG;
1505 }
1506 
1507 /* getobj callback for object to be invoked */
1508 static int
invoke_ok(struct obj * obj)1509 invoke_ok(struct obj *obj)
1510 {
1511     if (!obj)
1512         return GETOBJ_EXCLUDE;
1513 
1514     /* artifacts and other special items */
1515     if (obj->oartifact || objects[obj->otyp].oc_unique
1516         || (obj->otyp == FAKE_AMULET_OF_YENDOR && !obj->known))
1517         return GETOBJ_SUGGEST;
1518 
1519     /* synonym for apply, though actually invoking it will do different things
1520      * depending if it's a regular crystal ball, an artifact one that has an
1521      * invoke power, and a (theoretical) artifact one that doesn't have an
1522      * invoke power */
1523     if (obj->otyp == CRYSTAL_BALL)
1524         return GETOBJ_SUGGEST;
1525 
1526     return GETOBJ_EXCLUDE;
1527 }
1528 
1529 /* the #invoke command */
1530 int
doinvoke(void)1531 doinvoke(void)
1532 {
1533     struct obj *obj;
1534 
1535     obj = getobj("invoke", invoke_ok, GETOBJ_PROMPT);
1536     if (!obj)
1537         return 0;
1538     if (!retouch_object(&obj, FALSE, FALSE))
1539         return 1;
1540     return arti_invoke(obj);
1541 }
1542 
1543 static int
arti_invoke(struct obj * obj)1544 arti_invoke(struct obj *obj)
1545 {
1546     register const struct artifact *oart = get_artifact(obj);
1547     if (!obj) {
1548         impossible("arti_invoke without obj");
1549         return 0;
1550     }
1551     if (!oart || !oart->inv_prop) {
1552         if (obj->otyp == CRYSTAL_BALL)
1553             use_crystal_ball(&obj);
1554         else
1555             pline1(nothing_happens);
1556         return 1;
1557     }
1558 
1559     if (oart->inv_prop > LAST_PROP) {
1560         /* It's a special power, not "just" a property */
1561         if (obj->age > g.monstermoves) {
1562             /* the artifact is tired :-) */
1563             You_feel("that %s %s ignoring you.", the(xname(obj)),
1564                      otense(obj, "are"));
1565             if (!(wizard && yn("Override?") == 'y')) {
1566                 /* and just got more so; patience is essential... */
1567                 obj->age += (long) d(3, 10);
1568                 return 1;
1569             }
1570         }
1571         obj->age = g.monstermoves + rnz(100);
1572 
1573         switch (oart->inv_prop) {
1574         case TAMING: {
1575             struct obj pseudo;
1576 
1577             pseudo =
1578                 cg.zeroobj; /* neither cursed nor blessed, zero oextra too */
1579             pseudo.otyp = SCR_TAMING;
1580             (void) seffects(&pseudo);
1581             break;
1582         }
1583         case HEALING: {
1584             int healamt = (u.uhpmax + 1 - u.uhp) / 2;
1585             long creamed = (long) u.ucreamed;
1586 
1587             if (Upolyd)
1588                 healamt = (u.mhmax + 1 - u.mh) / 2;
1589             if (healamt || Sick || Slimed || Blinded > creamed)
1590                 You_feel("better.");
1591             else
1592                 goto nothing_special;
1593             if (healamt > 0) {
1594                 if (Upolyd)
1595                     u.mh += healamt;
1596                 else
1597                     u.uhp += healamt;
1598             }
1599             if (Sick)
1600                 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
1601             if (Slimed)
1602                 make_slimed(0L, (char *) 0);
1603             if (Blinded > creamed)
1604                 make_blinded(creamed, FALSE);
1605             g.context.botl = TRUE;
1606             break;
1607         }
1608         case ENERGY_BOOST: {
1609             int epboost = (u.uenmax + 1 - u.uen) / 2;
1610 
1611             if (epboost > 120)
1612                 epboost = 120; /* arbitrary */
1613             else if (epboost < 12)
1614                 epboost = u.uenmax - u.uen;
1615             if (epboost) {
1616                 u.uen += epboost;
1617                 g.context.botl = TRUE;
1618                 You_feel("re-energized.");
1619             } else
1620                 goto nothing_special;
1621             break;
1622         }
1623         case UNTRAP: {
1624             if (!untrap(TRUE)) {
1625                 obj->age = 0; /* don't charge for changing their mind */
1626                 return 0;
1627             }
1628             break;
1629         }
1630         case CHARGE_OBJ: {
1631             struct obj *otmp = getobj("charge", charge_ok,
1632                                       GETOBJ_PROMPT | GETOBJ_ALLOWCNT);
1633             boolean b_effect;
1634 
1635             if (!otmp) {
1636                 obj->age = 0;
1637                 return 0;
1638             }
1639             b_effect = (obj->blessed && (oart->role == Role_switch
1640                                          || oart->role == NON_PM));
1641             recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0);
1642             update_inventory();
1643             break;
1644         }
1645         case LEV_TELE:
1646             level_tele();
1647             break;
1648         case CREATE_PORTAL: {
1649             int i, num_ok_dungeons, last_ok_dungeon = 0;
1650             d_level newlev;
1651             winid tmpwin = create_nhwindow(NHW_MENU);
1652             anything any;
1653 
1654             any = cg.zeroany; /* set all bits to zero */
1655             start_menu(tmpwin, MENU_BEHAVE_STANDARD);
1656             /* use index+1 (cant use 0) as identifier */
1657             for (i = num_ok_dungeons = 0; i < g.n_dgns; i++) {
1658                 if (!g.dungeons[i].dunlev_ureached)
1659                     continue;
1660                 any.a_int = i + 1;
1661                 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
1662                          ATR_NONE, g.dungeons[i].dname, MENU_ITEMFLAGS_NONE);
1663                 num_ok_dungeons++;
1664                 last_ok_dungeon = i;
1665             }
1666             end_menu(tmpwin, "Open a portal to which dungeon?");
1667             if (num_ok_dungeons > 1) {
1668                 /* more than one entry; display menu for choices */
1669                 menu_item *selected;
1670                 int n;
1671 
1672                 n = select_menu(tmpwin, PICK_ONE, &selected);
1673                 if (n <= 0) {
1674                     destroy_nhwindow(tmpwin);
1675                     goto nothing_special;
1676                 }
1677                 i = selected[0].item.a_int - 1;
1678                 free((genericptr_t) selected);
1679             } else
1680                 i = last_ok_dungeon; /* also first & only OK dungeon */
1681             destroy_nhwindow(tmpwin);
1682 
1683             /*
1684              * i is now index into dungeon structure for the new dungeon.
1685              * Find the closest level in the given dungeon, open
1686              * a use-once portal to that dungeon and go there.
1687              * The closest level is either the entry or dunlev_ureached.
1688              */
1689             newlev.dnum = i;
1690             if (g.dungeons[i].depth_start >= depth(&u.uz))
1691                 newlev.dlevel = g.dungeons[i].entry_lev;
1692             else
1693                 newlev.dlevel = g.dungeons[i].dunlev_ureached;
1694 
1695             if (u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev)
1696                 || newlev.dnum == u.uz.dnum || !next_to_u()) {
1697                 You_feel("very disoriented for a moment.");
1698             } else {
1699                 if (!Blind)
1700                     You("are surrounded by a shimmering sphere!");
1701                 else
1702                     You_feel("weightless for a moment.");
1703                 goto_level(&newlev, FALSE, FALSE, FALSE);
1704             }
1705             break;
1706         }
1707         case ENLIGHTENING:
1708             enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS);
1709             break;
1710         case CREATE_AMMO: {
1711             struct obj *otmp = mksobj(ARROW, TRUE, FALSE);
1712 
1713             if (!otmp)
1714                 goto nothing_special;
1715             otmp->blessed = obj->blessed;
1716             otmp->cursed = obj->cursed;
1717             otmp->bknown = obj->bknown;
1718             if (obj->blessed) {
1719                 if (otmp->spe < 0)
1720                     otmp->spe = 0;
1721                 otmp->quan += rnd(10);
1722             } else if (obj->cursed) {
1723                 if (otmp->spe > 0)
1724                     otmp->spe = 0;
1725             } else
1726                 otmp->quan += rnd(5);
1727             otmp->owt = weight(otmp);
1728             otmp = hold_another_object(otmp, "Suddenly %s out.",
1729                                        aobjnam(otmp, "fall"), (char *) 0);
1730             nhUse(otmp);
1731             break;
1732         }
1733         case LIGHTNING_BOLT: {
1734             struct obj* pseudo = mksobj(WAN_LIGHTNING, FALSE, FALSE);
1735             pseudo->blessed = pseudo->cursed = 0;
1736             /* type is a "spell of lightning bolt" which doesn't actually
1737              * exist: 10 + AD_ELEC - 1 */
1738             if(!getdir(NULL) || (!u.dx && !u.dy && !u.dz)) {
1739                 int damage = zapyourself(pseudo, TRUE);
1740                 if (damage > 0) {
1741                     losehp(damage, "struck by lightning", NO_KILLER_PREFIX);
1742                 }
1743             }
1744             else {
1745                 /* don't use weffects - we want higher damage than that */
1746                 buzz(9 + AD_ELEC, 8, u.ux, u.uy, u.dx, u.dy);
1747             }
1748             obfree(pseudo, NULL);
1749             break;
1750         }
1751 	case SMOKE_CLOUD: {
1752 	    /* Itlachiayaque actually has two invoke effects - you can also gaze
1753 	     * into it like a crystal ball and look for a certain symbol. The
1754 	     * hero decides which effect. */
1755 	    int ret, n;
1756 	    char c;
1757             winid tmpwin = create_nhwindow(NHW_MENU);
1758             anything any = cg.zeroany;
1759             menu_item *selected;
1760 
1761             start_menu(tmpwin, MENU_BEHAVE_STANDARD);
1762             any.a_char = 'a';
1763             add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
1764                      "Create a stinking cloud", MENU_ITEMFLAGS_NONE);
1765             any.a_char = 'b';
1766             add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
1767                      "Gaze into the surface", MENU_ITEMFLAGS_NONE);
1768             end_menu(tmpwin, "What would you like to do?");
1769             n = select_menu(tmpwin, PICK_ONE, &selected);
1770             destroy_nhwindow(tmpwin);
1771 
1772             if (n < 0) {
1773                 obj->age = 0;
1774                 break;
1775             }
1776             c = selected[0].item.a_char;
1777             if (c == 'a') {
1778                 if (!any_quest_artifact(obj) || is_quest_artifact(obj)) {
1779                     You("may summon a stinking cloud.");
1780                 }
1781                 ret = do_stinking_cloud(obj, FALSE);
1782                 if (ret == SCLOUD_CANCELED) {
1783                     obj->age = 0;
1784                 }
1785             }
1786             else {
1787                 You("gaze into the polished surface...");
1788                 g.context.crystal.ball = obj;
1789                 g.context.crystal.o_id = obj->o_id;
1790                 g.context.crystal.looktime = rn1(5, 6); /* same as ball */
1791                 set_occupation(look_in_crystal_ball, "gazing", 0);
1792             }
1793             break;
1794 	}
1795         }
1796     } else {
1797         long eprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI),
1798              iprop = u.uprops[oart->inv_prop].intrinsic;
1799         boolean on = (eprop & W_ARTI) != 0; /* true if prop just set */
1800 
1801         if (on && obj->age > g.monstermoves) {
1802             /* the artifact is tired :-) */
1803             u.uprops[oart->inv_prop].extrinsic ^= W_ARTI;
1804             You_feel("that %s %s ignoring you.", the(xname(obj)),
1805                      otense(obj, "are"));
1806             if (!(wizard && yn("Override?") == 'y')) {
1807                 /* can't just keep repeatedly trying */
1808                 obj->age += (long) d(3, 10);
1809                 return 1;
1810             }
1811         } else if (!on) {
1812             /* when turning off property, determine downtime */
1813             /* arbitrary for now until we can tune this -dlc */
1814             obj->age = g.monstermoves + rnz(100);
1815         }
1816 
1817         if ((eprop & ~W_ARTI) || iprop) {
1818  nothing_special:
1819             /* you had the property from some other source too */
1820             if (carried(obj))
1821                 You_feel("a surge of power, but nothing seems to happen.");
1822             return 1;
1823         }
1824         switch (oart->inv_prop) {
1825         case CONFLICT:
1826             if (on)
1827                 You_feel("like a rabble-rouser.");
1828             else
1829                 You_feel("the tension decrease around you.");
1830             break;
1831         case LEVITATION:
1832             if (on) {
1833                 float_up();
1834                 spoteffects(FALSE);
1835             } else
1836                 (void) float_down(I_SPECIAL | TIMEOUT, W_ARTI);
1837             break;
1838         /* Formerly used for Orb of Detection, now unused.
1839         case INVIS:
1840             if (BInvis || Blind)
1841                 goto nothing_special;
1842             newsym(u.ux, u.uy);
1843             if (on)
1844                 Your("body takes on a %s transparency...",
1845                      Hallucination ? "normal" : "strange");
1846             else
1847                 Your("body seems to unfade...");
1848             break;
1849         */
1850         }
1851     }
1852 
1853     return 1;
1854 }
1855 
1856 /* will freeing this object from inventory cause levitation to end? */
1857 boolean
finesse_ahriman(struct obj * obj)1858 finesse_ahriman(struct obj *obj)
1859 {
1860     const struct artifact *oart;
1861     struct prop save_Lev;
1862     boolean result;
1863 
1864     /* if we aren't levitating or this isn't an artifact which confers
1865        levitation via #invoke then freeinv() won't toggle levitation */
1866     if (!Levitation || (oart = get_artifact(obj)) == 0
1867         || oart->inv_prop != LEVITATION || !(ELevitation & W_ARTI))
1868         return FALSE;
1869 
1870     /* arti_invoke(off) -> float_down() clears I_SPECIAL|TIMEOUT & W_ARTI;
1871        probe ahead to see whether that actually results in floating down;
1872        (this assumes that there aren't two simultaneously invoked artifacts
1873        both conferring levitation--safe, since if there were two of them,
1874        invoking the 2nd would negate the 1st rather than stack with it) */
1875     save_Lev = u.uprops[LEVITATION];
1876     HLevitation &= ~(I_SPECIAL | TIMEOUT);
1877     ELevitation &= ~W_ARTI;
1878     result = (boolean) !Levitation;
1879     u.uprops[LEVITATION] = save_Lev;
1880     return result;
1881 }
1882 
1883 /* WAC return TRUE if artifact is always lit */
1884 boolean
artifact_light(struct obj * obj)1885 artifact_light(struct obj *obj)
1886 {
1887     return (boolean) (get_artifact(obj) && obj->oartifact == ART_SUNSWORD);
1888 }
1889 
1890 /* KMH -- Talking artifacts are finally implemented */
1891 void
arti_speak(struct obj * obj)1892 arti_speak(struct obj *obj)
1893 {
1894     register const struct artifact *oart = get_artifact(obj);
1895     const char *line;
1896     char buf[BUFSZ];
1897 
1898     /* Is this a speaking artifact? */
1899     if (!oart || !(oart->spfx & SPFX_SPEAK))
1900         return;
1901 
1902     line = getrumor(bcsign(obj), buf, TRUE);
1903     if (!*line)
1904         line = "NetHack rumors file closed for renovation.";
1905     pline("%s:", Tobjnam(obj, "whisper"));
1906     verbalize1(line);
1907     return;
1908 }
1909 
1910 boolean
artifact_has_invprop(struct obj * otmp,uchar inv_prop)1911 artifact_has_invprop(struct obj *otmp, uchar inv_prop)
1912 {
1913     const struct artifact *arti = get_artifact(otmp);
1914 
1915     return (boolean) (arti && (arti->inv_prop == inv_prop));
1916 }
1917 
1918 /* Return the price sold to the hero of a given artifact or unique item */
1919 long
arti_cost(struct obj * otmp)1920 arti_cost(struct obj *otmp)
1921 {
1922     if (!otmp->oartifact)
1923         return (long) objects[otmp->otyp].oc_cost;
1924     else if (artilist[(int) otmp->oartifact].cost)
1925         return artilist[(int) otmp->oartifact].cost;
1926     else
1927         return (100L * (long) objects[otmp->otyp].oc_cost);
1928 }
1929 
1930 static uchar
abil_to_adtyp(long * abil)1931 abil_to_adtyp(long *abil)
1932 {
1933     struct abil2adtyp_tag {
1934         long *abil;
1935         uchar adtyp;
1936     } abil2adtyp[] = {
1937         { &EFire_resistance, AD_FIRE },
1938         { &ECold_resistance, AD_COLD },
1939         { &EShock_resistance, AD_ELEC },
1940         { &EAntimagic, AD_MAGM },
1941         { &EDisint_resistance, AD_DISN },
1942         { &EPoison_resistance, AD_DRST },
1943         { &EDrain_resistance, AD_DRLI },
1944     };
1945     int k;
1946 
1947     for (k = 0; k < SIZE(abil2adtyp); k++) {
1948         if (abil2adtyp[k].abil == abil)
1949             return abil2adtyp[k].adtyp;
1950     }
1951     return 0;
1952 }
1953 
1954 static unsigned long
abil_to_spfx(long * abil)1955 abil_to_spfx(long *abil)
1956 {
1957     static const struct abil2spfx_tag {
1958         long *abil;
1959         unsigned long spfx;
1960     } abil2spfx[] = {
1961         { &ESearching, SPFX_SEARCH },
1962         { &EHalluc_resistance, SPFX_HALRES },
1963         { &ETelepat, SPFX_ESP },
1964         { &EStealth, SPFX_STLTH },
1965         { &ERegeneration, SPFX_REGEN },
1966         { &ETeleport_control, SPFX_TCTRL },
1967         { &EWarn_of_mon, SPFX_WARN },
1968         { &EWarning, SPFX_WARN },
1969         { &EEnergy_regeneration, SPFX_EREGEN },
1970         { &EHalf_spell_damage, SPFX_HSPDAM },
1971         { &EHalf_physical_damage, SPFX_HPHDAM },
1972         { &EReflecting, SPFX_REFLECT },
1973     };
1974     int k;
1975 
1976     for (k = 0; k < SIZE(abil2spfx); k++) {
1977         if (abil2spfx[k].abil == abil)
1978             return abil2spfx[k].spfx;
1979     }
1980     return 0L;
1981 }
1982 
1983 /*
1984  * Return the first item that is conveying a particular intrinsic.
1985  */
1986 struct obj *
what_gives(long * abil)1987 what_gives(long *abil)
1988 {
1989     struct obj *obj;
1990     uchar dtyp;
1991     unsigned long spfx;
1992     long wornbits;
1993     long wornmask = (W_ARM | W_ARMC | W_ARMH | W_ARMS
1994                      | W_ARMG | W_ARMF | W_ARMU
1995                      | W_AMUL | W_RINGL | W_RINGR | W_TOOL
1996                      | W_ART | W_ARTI);
1997 
1998     if (u.twoweap)
1999         wornmask |= W_SWAPWEP;
2000     dtyp = abil_to_adtyp(abil);
2001     spfx = abil_to_spfx(abil);
2002     wornbits = (wornmask & *abil);
2003 
2004     for (obj = g.invent; obj; obj = obj->nobj) {
2005         if (obj->oartifact
2006             && (abil != &EWarn_of_mon || g.context.warntype.obj
2007                 || g.context.warntype.obj_mlet)) {
2008             const struct artifact *art = get_artifact(obj);
2009 
2010             if (art) {
2011                 if (dtyp) {
2012                     if (art->cary.adtyp == dtyp /* carried */
2013                         || (art->defn.adtyp == dtyp /* defends while worn */
2014                             && (obj->owornmask & ~(W_ART | W_ARTI))))
2015                         return obj;
2016                 }
2017                 if (spfx) {
2018                     /* property conferred when carried */
2019                     if ((art->cspfx & spfx) == spfx)
2020                         return obj;
2021                     /* property conferred when wielded or worn */
2022                     if ((art->spfx & spfx) == spfx && obj->owornmask)
2023                         return obj;
2024                 }
2025             }
2026         } else {
2027             if (wornbits && wornbits == (wornmask & obj->owornmask))
2028                 return obj;
2029         }
2030     }
2031     return (struct obj *) 0;
2032 }
2033 
2034 const char *
glow_color(int arti_indx)2035 glow_color(int arti_indx)
2036 {
2037     int colornum = artilist[arti_indx].acolor;
2038     const char *colorstr = clr2colorname(colornum);
2039 
2040     return hcolor(colorstr);
2041 }
2042 
2043 /* glow verb; [0] holds the value used when blind */
2044 static const char *glow_verbs[] = {
2045     "quiver", "flicker", "glimmer", "gleam"
2046 };
2047 
2048 /* relative strength that Sting is glowing (0..3), to select verb */
2049 static int
glow_strength(int count)2050 glow_strength(int count)
2051 {
2052     /* glow strength should also be proportional to proximity and
2053        probably difficulty, but we don't have that information and
2054        gathering it is more trouble than this would be worth */
2055     return (count > 12) ? 3 : (count > 4) ? 2 : (count > 0);
2056 }
2057 
2058 const char *
glow_verb(int count,boolean ingsfx)2059 glow_verb(int count, /* 0 means blind rather than no applicable creatures */
2060           boolean ingsfx)
2061 {
2062     static char resbuf[20];
2063 
2064     Strcpy(resbuf, glow_verbs[glow_strength(count)]);
2065     /* ing_suffix() will double the last consonant for all the words
2066        we're using and none of them should have that, so bypass it */
2067     if (ingsfx)
2068         Strcat(resbuf, "ing");
2069     return resbuf;
2070 }
2071 
2072 /* use for warning "glow" for Sting, Orcrist, and Grimtooth */
2073 void
Sting_effects(int orc_count)2074 Sting_effects(int orc_count) /* new count (warn_obj_cnt is old count); -1 is a flag value */
2075 {
2076     if (uwep && uwep->oartifact
2077         && artilist[(int) uwep->oartifact].acolor != NO_COLOR) {
2078         int oldstr = glow_strength(g.warn_obj_cnt),
2079             newstr = glow_strength(orc_count);
2080 
2081         if (orc_count == -1 && g.warn_obj_cnt > 0) {
2082             /* -1 means that blindness has just been toggled; give a
2083                'continue' message that eventual 'stop' message will match */
2084             pline("%s is %s.", bare_artifactname(uwep),
2085                   glow_verb(Blind ? 0 : g.warn_obj_cnt, TRUE));
2086         } else if (newstr > 0 && newstr != oldstr) {
2087             /* goto_level() -> docrt() -> see_monsters() -> Sting_effects();
2088                if "you materialize on a different level" is pending, give
2089                it now so that start-glowing message comes after it */
2090             maybe_lvltport_feedback(); /* usually called by goto_level() */
2091 
2092             /* 'start' message */
2093             if (!Blind)
2094                 pline("%s %s %s%c", bare_artifactname(uwep),
2095                       otense(uwep, glow_verb(orc_count, FALSE)),
2096                       glow_color(uwep->oartifact),
2097                       (newstr > oldstr) ? '!' : '.');
2098             else if (oldstr == 0) /* quivers */
2099                 pline("%s %s slightly.", bare_artifactname(uwep),
2100                       otense(uwep, glow_verb(0, FALSE)));
2101         } else if (orc_count == 0 && g.warn_obj_cnt > 0) {
2102             /* 'stop' message */
2103             pline("%s stops %s.", bare_artifactname(uwep),
2104                   glow_verb(Blind ? 0 : g.warn_obj_cnt, TRUE));
2105         }
2106     }
2107 }
2108 
2109 /* called when hero is wielding/applying/invoking a carried item, or
2110    after undergoing a transformation (alignment change, lycanthropy,
2111    polymorph) which might affect item access */
2112 int
retouch_object(struct obj ** objp,boolean loseit,boolean protected_by_gear)2113 retouch_object(struct obj **objp, /* might be destroyed or unintentionally dropped */
2114                boolean loseit,    /* whether to drop it if hero can longer touch it */
2115                boolean protected_by_gear) /* whether the player has gear that
2116                                            * will protect them from touching
2117                                            * *obj if it is a hated material */
2118 {
2119     struct obj *obj = *objp;
2120 
2121     /* allow hero in silver-hating form to try to perform invocation ritual */
2122     if (obj->otyp == BELL_OF_OPENING
2123         && invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
2124         return 1;
2125     }
2126 
2127     if (touch_artifact(obj, &g.youmonst)) {
2128         char buf[BUFSZ];
2129         int dmg = 0;
2130         boolean hatemat = Hate_material(obj->material),
2131                 bane = bane_applies(get_artifact(obj), &g.youmonst);
2132 
2133         /* nothing else to do if hero can successfully handle this object */
2134         if (!hatemat && !bane)
2135             return 1;
2136 
2137         /* another case where nothing should happen: hero is wearing gloves
2138          * which protect them from directly touching a weapon of a material they
2139          * hate
2140          * (no other gear slots are considered to completely block touching an
2141          * outer piece of gear; e.g. wearing body armor doesn't protect from
2142          * touching a worn cloak) */
2143         if (!bane && protected_by_gear)
2144             return 1;
2145 
2146         /* hero can't handle this object, but didn't get touch_artifact()'s
2147            "<obj> evades your grasp|control" message; give an alternate one */
2148 
2149         if (!bane && !(hatemat && obj->material == SILVER)) {
2150             pline("The %s of %s hurts to touch!", materialnm[obj->material],
2151                   yname(obj));
2152         }
2153         else {
2154             You_cant("handle %s%s!", yname(obj),
2155                     obj->owornmask ? " anymore" : "");
2156         }
2157         /* also inflict damage unless touch_artifact() already did so */
2158         if (!touch_blasted) {
2159             /* damage is somewhat arbitrary: 1d10 magical for <foo>bane,
2160              * half of the usual damage for materials */
2161             if (hatemat)
2162                 dmg += rnd(sear_damage(obj->material) / 2);
2163             if (bane)
2164                 dmg += rnd(10);
2165             Sprintf(buf, "handling %s", killer_xname(obj));
2166             losehp(dmg, buf, KILLED_BY);
2167             exercise(A_CON, FALSE);
2168         }
2169         /* concession to elves wishing to use iron gear: don't make them
2170          * totally unable to use them. In fact, they can touch them just fine
2171          * as long as they're willing to.
2172          * In keeping with the flavor of searing vs just pain implemented
2173          * everywhere else, only silver is actually unbearable -- other
2174          * hated non-silver materials can be used too. */
2175         if (!bane && !(hatemat && obj->material == SILVER))
2176             return 1;
2177     }
2178 
2179     /* removing a worn item might result in loss of levitation,
2180        dropping the hero onto a polymorph trap or into water or
2181        lava and potentially dropping or destroying the item */
2182     if (obj->owornmask) {
2183         struct obj *otmp;
2184 
2185         remove_worn_item(obj, FALSE);
2186         for (otmp = g.invent; otmp; otmp = otmp->nobj)
2187             if (otmp == obj)
2188                 break;
2189         if (!otmp)
2190             *objp = obj = 0;
2191     }
2192 
2193     /* if we still have it and caller wants us to drop it, do so now */
2194     if (loseit && obj) {
2195         if (Levitation) {
2196             freeinv(obj);
2197             hitfloor(obj, TRUE);
2198         } else {
2199             /* dropx gives a message iff item lands on an altar */
2200             if (!IS_ALTAR(levl[u.ux][u.uy].typ))
2201                 pline("%s to the %s.", Tobjnam(obj, "fall"),
2202                       surface(u.ux, u.uy));
2203             dropx(obj);
2204         }
2205         *objp = obj = 0; /* no longer in inventory */
2206     }
2207     return 0;
2208 }
2209 
2210 /* an item which is worn/wielded or an artifact which conveys
2211    something via being carried or which has an #invoke effect
2212    currently in operation undergoes a touch test; if it fails,
2213    it will be unworn/unwielded and revoked but not dropped */
2214 static boolean
untouchable(struct obj * obj,boolean drop_untouchable)2215 untouchable(struct obj *obj, boolean drop_untouchable)
2216 {
2217     struct artifact *art;
2218     boolean beingworn, carryeffect, invoked;
2219     long wearmask = ~(W_QUIVER | (u.twoweap ? 0L : W_SWAPWEP) | W_BALL);
2220 
2221     beingworn = ((obj->owornmask & wearmask) != 0L
2222                  /* some items in use don't have any wornmask setting */
2223                  || (obj->oclass == TOOL_CLASS
2224                      && (obj->lamplit || (obj->otyp == LEASH && obj->leashmon)
2225                          || (Is_container(obj) && Has_contents(obj)))));
2226 
2227     if ((art = get_artifact(obj)) != 0) {
2228         carryeffect = (art->cary.adtyp || art->cspfx);
2229         invoked = (art->inv_prop > 0 && art->inv_prop <= LAST_PROP
2230                    && (u.uprops[art->inv_prop].extrinsic & W_ARTI) != 0L);
2231     } else {
2232         carryeffect = invoked = FALSE;
2233     }
2234 
2235     if (beingworn || carryeffect || invoked) {
2236         if (!retouch_object(&obj, drop_untouchable,
2237                             !will_touch_skin(obj->owornmask & wearmask))) {
2238             /* "<artifact> is beyond your control" or "you can't handle
2239                <object>" has been given and it is now unworn/unwielded
2240                and possibly dropped (depending upon caller); if dropped,
2241                carried effect was turned off, else we leave that alone;
2242                we turn off invocation property here if still carried */
2243             if (invoked && obj)
2244                 arti_invoke(obj); /* reverse #invoke */
2245             return TRUE;
2246         }
2247     }
2248     return FALSE;
2249 }
2250 
2251 /* check all items currently in use (mostly worn) for touchability */
2252 void
retouch_equipment(int dropflag)2253 retouch_equipment(int dropflag) /* 0==don't drop, 1==drop all, 2==drop weapon */
2254 {
2255     static int nesting = 0; /* recursion control */
2256     struct obj *obj;
2257     boolean dropit, had_gloves = (uarmg != 0);
2258     int had_rings = (!!uleft + !!uright);
2259 
2260     /*
2261      * We can potentially be called recursively if losing/unwearing
2262      * an item causes worn helm of opposite alignment to come off or
2263      * be destroyed.
2264      *
2265      * BUG: if the initial call was due to putting on a helm of
2266      * opposite alignment and it does come off to trigger recursion,
2267      * after the inner call executes, the outer call will finish
2268      * using the non-helm alignment rather than the helm alignment
2269      * which triggered this in the first place.
2270      */
2271     if (!nesting++)
2272         clear_bypasses(); /* init upon initial entry */
2273 
2274     dropit = (dropflag > 0); /* drop all or drop weapon */
2275     /* check secondary weapon first, before possibly unwielding primary */
2276     if (u.twoweap) {
2277         bypass_obj(uswapwep); /* so loop below won't process it again */
2278         (void) untouchable(uswapwep, dropit);
2279     }
2280     /* check primary weapon next so that they're handled together */
2281     if (uwep) {
2282         bypass_obj(uwep); /* so loop below won't process it again */
2283         (void) untouchable(uwep, dropit);
2284     }
2285 
2286     /* in case someone is daft enough to add artifact or silver saddle */
2287     if (u.usteed && (obj = which_armor(u.usteed, W_SADDLE)) != 0) {
2288         /* untouchable() calls retouch_object() which expects an object in
2289            hero's inventory, but remove_worn_item() will be harmless for
2290            saddle and we're suppressing drop, so this works as intended */
2291         if (untouchable(obj, FALSE))
2292             dismount_steed(DISMOUNT_THROWN);
2293     }
2294     /*
2295      * TODO?  Force off gloves if either or both rings are going to
2296      * become unworn; force off cloak [suit] before suit [shirt].
2297      * The torso handling is hypothetical; the case for gloves is
2298      * not, due to the possibility of unwearing silver rings.
2299      */
2300 
2301     dropit = (dropflag == 1); /* all untouchable items */
2302     /* loss of levitation (silver ring, or Heart of Ahriman invocation)
2303        might cause hero to lose inventory items (by dropping into lava,
2304        for instance), so inventory traversal needs to rescan the whole
2305        g.invent chain each time it moves on to another object; we use bypass
2306        handling to keep track of which items have already been processed */
2307     while ((obj = nxt_unbypassed_obj(g.invent)) != 0)
2308         (void) untouchable(obj, dropit);
2309 
2310     if (had_rings != (!!uleft + !!uright) && uarmg && uarmg->cursed)
2311         uncurse(uarmg); /* temporary? hack for ring removal plausibility */
2312     if (had_gloves && !uarmg)
2313         selftouch("After losing your gloves, you");
2314 
2315     if (!--nesting)
2316         clear_bypasses(); /* reset upon final exit */
2317 }
2318 
2319 static int
count_surround_traps(int x,int y)2320 count_surround_traps(int x, int y)
2321 {
2322     struct rm *levp;
2323     struct obj *otmp;
2324     struct trap *ttmp;
2325     int dx, dy, glyph, ret = 0;
2326 
2327     for (dx = x - 1; dx < x + 2; ++dx)
2328         for (dy = y - 1; dy < y + 2; ++dy) {
2329             if (!isok(dx, dy))
2330                 continue;
2331             /* If a trap is shown here, don't count it; the hero
2332              * should be expecting it.  But if there is a trap here
2333              * that's not shown, either undiscovered or covered by
2334              * something, do count it.
2335              */
2336             glyph = glyph_at(dx, dy);
2337             if (glyph_is_trap(glyph))
2338                 continue;
2339             if ((ttmp = t_at(dx, dy)) != 0) {
2340                 ++ret;
2341                 continue;
2342             }
2343             levp = &levl[dx][dy];
2344             if (IS_DOOR(levp->typ) && door_is_trapped(levp)) {
2345                 ++ret;
2346                 continue;
2347             }
2348             for (otmp = g.level.objects[dx][dy]; otmp; otmp = otmp->nexthere)
2349                 if (Is_container(otmp) && otmp->otrapped) {
2350                     ++ret; /* we're counting locations, so just */
2351                     break; /* count the first one in a pile     */
2352                 }
2353         }
2354     /*
2355      * [Shouldn't we also check inventory for a trapped container?
2356      * Even if its trap has already been found, there's no 'tknown'
2357      * flag to help hero remember that so we have nothing comparable
2358      * to a shown glyph to justify skipping it.]
2359      */
2360     return ret;
2361 }
2362 
2363 /* sense adjacent traps if wielding MKoT without wearing gloves */
2364 void
mkot_trap_warn(void)2365 mkot_trap_warn(void)
2366 {
2367     static const char *const heat[7] = {
2368         "cool", "slightly warm", "warm", "very warm",
2369         "hot", "very hot", "like fire"
2370     };
2371 
2372     if (!uarmg && uwep && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) {
2373         int idx, ntraps = count_surround_traps(u.ux, u.uy);
2374 
2375         if (ntraps != g.mkot_trap_warn_count) {
2376             idx = min(ntraps, SIZE(heat) - 1);
2377             pline_The("Key feels %s%c", heat[idx], (ntraps > 3) ? '!' : '.');
2378         }
2379         g.mkot_trap_warn_count = ntraps;
2380     } else
2381         g.mkot_trap_warn_count = 0;
2382 }
2383 
2384 /* Master Key is magic key if its bless/curse state meets our criteria:
2385    not cursed for rogues or blessed for non-rogues */
2386 boolean
is_magic_key(struct monst * mon,struct obj * obj)2387 is_magic_key(struct monst *mon, /* if null, non-rogue is assumed */
2388              struct obj *obj)
2389 {
2390     if (obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY) {
2391         if ((mon == &g.youmonst) ? Role_if(PM_ROGUE)
2392                                  : (mon && mon->data == &mons[PM_ROGUE]))
2393             return !obj->cursed; /* a rogue; non-cursed suffices for magic */
2394         /* not a rogue; key must be blessed to behave as a magic one */
2395         return obj->blessed;
2396     }
2397     return FALSE;
2398 }
2399 
2400 /* figure out whether 'mon' (usually youmonst) is carrying the magic key */
2401 struct obj *
has_magic_key(struct monst * mon)2402 has_magic_key(struct monst *mon) /* if null, hero assumed */
2403 {
2404     struct obj *o;
2405     short key = artilist[ART_MASTER_KEY_OF_THIEVERY].otyp;
2406 
2407     if (!mon)
2408         mon = &g.youmonst;
2409     for (o = ((mon == &g.youmonst) ? g.invent : mon->minvent); o;
2410          o = nxtobj(o, key, FALSE)) {
2411         if (is_magic_key(mon, o))
2412             return o;
2413     }
2414     return (struct obj *) 0;
2415 }
2416 
2417 /* return TRUE if obj is permanently poisoned (currently only true for artifacts
2418  * in general and Grimtooth specifically) */
2419 boolean
permapoisoned(struct obj * obj)2420 permapoisoned(struct obj *obj)
2421 {
2422     return (obj && obj->oartifact == ART_GRIMTOOTH);
2423 }
2424 
2425 /* return TRUE if obj is an artifact with a name like "The X [of Y]" */
2426 boolean
arti_starts_with_the(struct obj * obj)2427 arti_starts_with_the(struct obj *obj)
2428 {
2429     const char *artname;
2430     if (!obj->oartifact)
2431         return FALSE;
2432 
2433     artname = artiname(obj->oartifact);
2434     if (!strncmp(artname, "The", 3)) {
2435         return TRUE;
2436     }
2437     return FALSE;
2438 }
2439 
2440 /*artifact.c*/
2441