OSDN Git Service

no E-word
[nethackexpress/trunk.git] / src / artifact.c
1 /*      SCCS Id: @(#)artifact.c 3.4     2003/08/11      */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #include "hack.h"
6 #include "artifact.h"
7 #ifdef OVLB
8 #include "artilist.h"
9 #else
10 STATIC_DCL struct artifact artilist[];
11 #endif
12 /*
13  * Note:  both artilist[] and artiexist[] have a dummy element #0,
14  *        so loops over them should normally start at #1.  The primary
15  *        exception is the save & restore code, which doesn't care about
16  *        the contents, just the total size.
17  */
18
19 extern boolean notonhead;       /* for long worms */
20
21 #define get_artifact(o) \
22                 (((o)&&(o)->oartifact) ? &artilist[(int) (o)->oartifact] : 0)
23
24 STATIC_DCL int FDECL(spec_applies, (const struct artifact *,struct monst *));
25 STATIC_DCL int FDECL(arti_invoke, (struct obj*));
26 STATIC_DCL boolean FDECL(Mb_hit, (struct monst *magr,struct monst *mdef,
27                                   struct obj *,int *,int,BOOLEAN_P,char *));
28
29 /* The amount added to the victim's total hit points to insure that the
30    victim will be killed even after damage bonus/penalty adjustments.
31    Most such penalties are small, and 200 is plenty; the exception is
32    half physical damage.  3.3.1 and previous versions tried to use a very
33    large number to account for this case; now, we just compute the fatal
34    damage by adding it to 2 times the total hit points instead of 1 time.
35    Note: this will still break if they have more than about half the number
36    of hit points that will fit in a 15 bit integer. */
37 #define FATAL_DAMAGE_MODIFIER 200
38
39 #ifndef OVLB
40 STATIC_DCL int spec_dbon_applies;
41 STATIC_DCL xchar artidisco[NROFARTIFACTS];
42 #else   /* OVLB */
43 /* coordinate effects from spec_dbon() with messages in artifact_hit() */
44 STATIC_OVL int spec_dbon_applies = 0;
45
46 /* flags including which artifacts have already been created */
47 static boolean artiexist[1+NROFARTIFACTS+1];
48 /* and a discovery list for them (no dummy first entry here) */
49 STATIC_OVL xchar artidisco[NROFARTIFACTS];
50
51 STATIC_DCL void NDECL(hack_artifacts);
52 STATIC_DCL boolean FDECL(attacks, (int,struct obj *));
53
54 /* handle some special cases; must be called after u_init() */
55 STATIC_OVL void
56 hack_artifacts()
57 {
58         struct artifact *art;
59         int alignmnt = aligns[flags.initalign].value;
60
61         /* Fix up the alignments of "gift" artifacts */
62         for (art = artilist+1; art->otyp; art++)
63             if (art->role == Role_switch && art->alignment != A_NONE)
64                 art->alignment = alignmnt;
65
66         /* Excalibur can be used by any lawful character, not just knights */
67         if (!Role_if(PM_KNIGHT))
68             artilist[ART_EXCALIBUR].role = NON_PM;
69
70         /* Fix up the quest artifact */
71         if (urole.questarti) {
72             artilist[urole.questarti].alignment = alignmnt;
73             artilist[urole.questarti].role = Role_switch;
74         }
75         return;
76 }
77
78 /* zero out the artifact existence list */
79 void
80 init_artifacts()
81 {
82         (void) memset((genericptr_t) artiexist, 0, sizeof artiexist);
83         (void) memset((genericptr_t) artidisco, 0, sizeof artidisco);
84         hack_artifacts();
85 }
86
87 void
88 save_artifacts(fd)
89 int fd;
90 {
91         bwrite(fd, (genericptr_t) artiexist, sizeof artiexist);
92         bwrite(fd, (genericptr_t) artidisco, sizeof artidisco);
93 }
94
95 void
96 restore_artifacts(fd)
97 int fd;
98 {
99         mread(fd, (genericptr_t) artiexist, sizeof artiexist);
100         mread(fd, (genericptr_t) artidisco, sizeof artidisco);
101         hack_artifacts();       /* redo non-saved special cases */
102 }
103
104 const char *
105 artiname(artinum)
106 int artinum;
107 {
108         if (artinum <= 0 || artinum > NROFARTIFACTS) return("");
109         return(artilist[artinum].name);
110 }
111
112 /*
113    Make an artifact.  If a specific alignment is specified, then an object of
114    the appropriate alignment is created from scratch, or 0 is returned if
115    none is available.  (If at least one aligned artifact has already been
116    given, then unaligned ones also become eligible for this.)
117    If no alignment is given, then 'otmp' is converted
118    into an artifact of matching type, or returned as-is if that's not possible.
119    For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE);''
120    for the 1st, ``obj = mk_artifact((struct obj *)0, some_alignment);''.
121  */
122 struct obj *
123 mk_artifact(otmp, alignment)
124 struct obj *otmp;       /* existing object; ignored if alignment specified */
125 aligntyp alignment;     /* target alignment, or A_NONE */
126 {
127         const struct artifact *a;
128         int n, m;
129         boolean by_align = (alignment != A_NONE);
130         short o_typ = (by_align || !otmp) ? 0 : otmp->otyp;
131         boolean unique = !by_align && otmp && objects[o_typ].oc_unique;
132         short eligible[NROFARTIFACTS];
133
134         /* gather eligible artifacts */
135         for (n = 0, a = artilist+1, m = 1; a->otyp; a++, m++)
136             if ((!by_align ? a->otyp == o_typ :
137                     (a->alignment == alignment ||
138                         (a->alignment == A_NONE && u.ugifts > 0))) &&
139                 (!(a->spfx & SPFX_NOGEN) || unique) && !artiexist[m]) {
140                 if (by_align && a->race != NON_PM && race_hostile(&mons[a->race]))
141                     continue;   /* skip enemies' equipment */
142                 else if (by_align && Role_if(a->role))
143                     goto make_artif;    /* 'a' points to the desired one */
144                 else
145                     eligible[n++] = m;
146             }
147
148         if (n) {                /* found at least one candidate */
149             m = eligible[rn2(n)];       /* [0..n-1] */
150             a = &artilist[m];
151
152             /* make an appropriate object if necessary, then christen it */
153 make_artif: if (by_align) otmp = mksobj((int)a->otyp, TRUE, FALSE);
154             otmp = oname(otmp, a->name);
155             otmp->oartifact = m;
156             artiexist[m] = TRUE;
157         } else {
158             /* nothing appropriate could be found; return the original object */
159             if (by_align) otmp = 0;     /* (there was no original object) */
160         }
161         return otmp;
162 }
163
164 /*
165  * Returns the full name (with articles and correct capitalization) of an
166  * artifact named "name" if one exists, or NULL, it not.
167  * The given name must be rather close to the real name for it to match.
168  * The object type of the artifact is returned in otyp if the return value
169  * is non-NULL.
170  */
171 const char*
172 artifact_name(name, otyp)
173 const char *name;
174 short *otyp;
175 {
176     register const struct artifact *a;
177     register const char *aname;
178
179     if(!strncmpi(name, "the ", 4)) name += 4;
180
181     for (a = artilist+1; a->otyp; a++) {
182         aname = a->name;
183         if(!strncmpi(aname, "the ", 4)) aname += 4;
184         if(!strcmpi(name, aname)) {
185             *otyp = a->otyp;
186             return a->name;
187         }
188     }
189
190     return (char *)0;
191 }
192
193 boolean
194 exist_artifact(otyp, name)
195 register int otyp;
196 register const char *name;
197 {
198         register const struct artifact *a;
199         register boolean *arex;
200
201         if (otyp && *name)
202             for (a = artilist+1,arex = artiexist+1; a->otyp; a++,arex++)
203                 if ((int) a->otyp == otyp && !strcmp(a->name, name))
204                     return *arex;
205         return FALSE;
206 }
207
208 void
209 artifact_exists(otmp, name, mod)
210 register struct obj *otmp;
211 register const char *name;
212 register boolean mod;
213 {
214         register const struct artifact *a;
215
216         if (otmp && *name)
217             for (a = artilist+1; a->otyp; a++)
218                 if (a->otyp == otmp->otyp && !strcmp(a->name, name)) {
219                     register int m = a - artilist;
220                     otmp->oartifact = (char)(mod ? m : 0);
221                     otmp->age = 0;
222                     if(otmp->otyp == RIN_INCREASE_DAMAGE)
223                         otmp->spe = 0;
224                     artiexist[m] = mod;
225                     break;
226                 }
227         return;
228 }
229
230 int
231 nartifact_exist()
232 {
233     int a = 0;
234     int n = SIZE(artiexist);
235
236     while(n > 1)
237         if(artiexist[--n]) a++;
238
239     return a;
240 }
241 #endif /* OVLB */
242 #ifdef OVL0
243
244 boolean
245 spec_ability(otmp, abil)
246 struct obj *otmp;
247 unsigned long abil;
248 {
249         const struct artifact *arti = get_artifact(otmp);
250
251         return((boolean)(arti && (arti->spfx & abil)));
252 }
253
254 /* used so that callers don't need to known about SPFX_ codes */
255 boolean
256 confers_luck(obj)
257 struct obj *obj;
258 {
259     /* might as well check for this too */
260     if (obj->otyp == LUCKSTONE) return TRUE;
261
262     return (obj->oartifact && spec_ability(obj, SPFX_LUCK));
263 }
264
265 /* used to check whether a monster is getting reflection from an artifact */
266 boolean
267 arti_reflects(obj)
268 struct obj *obj;
269 {
270     const struct artifact *arti = get_artifact(obj);
271
272     if (arti) {      
273         /* while being worn */
274         if ((obj->owornmask & ~W_ART) && (arti->spfx & SPFX_REFLECT))
275             return TRUE;
276         /* just being carried */
277         if (arti->cspfx & SPFX_REFLECT) return TRUE;
278     }
279     return FALSE;
280 }
281
282 #endif /* OVL0 */
283 #ifdef OVLB
284
285 boolean
286 restrict_name(otmp, name)  /* returns 1 if name is restricted for otmp->otyp */
287 register struct obj *otmp;
288 register const char *name;
289 {
290         register const struct artifact *a;
291         register const char *aname;
292
293         if (!*name) return FALSE;
294         if (!strncmpi(name, "the ", 4)) name += 4;
295
296                 /* Since almost every artifact is SPFX_RESTR, it doesn't cost
297                    us much to do the string comparison before the spfx check.
298                    Bug fix:  don't name multiple elven daggers "Sting".
299                  */
300         for (a = artilist+1; a->otyp; a++) {
301             if (a->otyp != otmp->otyp) continue;
302             aname = a->name;
303             if (!strncmpi(aname, "the ", 4)) aname += 4;
304             if (!strcmp(aname, name))
305                 return ((boolean)((a->spfx & (SPFX_NOGEN|SPFX_RESTR)) != 0 ||
306                         otmp->quan > 1L));
307         }
308
309         return FALSE;
310 }
311
312 STATIC_OVL boolean
313 attacks(adtyp, otmp)
314 register int adtyp;
315 register struct obj *otmp;
316 {
317         register const struct artifact *weap;
318
319         if ((weap = get_artifact(otmp)) != 0)
320                 return((boolean)(weap->attk.adtyp == adtyp));
321         return FALSE;
322 }
323
324 boolean
325 defends(adtyp, otmp)
326 register int adtyp;
327 register struct obj *otmp;
328 {
329         register const struct artifact *weap;
330
331         if ((weap = get_artifact(otmp)) != 0)
332                 return((boolean)(weap->defn.adtyp == adtyp));
333         return FALSE;
334 }
335
336 /* used for monsters */
337 boolean
338 protects(adtyp, otmp)
339 int adtyp;
340 struct obj *otmp;
341 {
342         register const struct artifact *weap;
343
344         if ((weap = get_artifact(otmp)) != 0)
345                 return (boolean)(weap->cary.adtyp == adtyp);
346         return FALSE;
347 }
348
349 /*
350  * a potential artifact has just been worn/wielded/picked-up or
351  * unworn/unwielded/dropped.  Pickup/drop only set/reset the W_ART mask.
352  */
353 void
354 set_artifact_intrinsic(otmp,on,wp_mask)
355 register struct obj *otmp;
356 boolean on;
357 long wp_mask;
358 {
359         long *mask = 0;
360         register const struct artifact *oart = get_artifact(otmp);
361         uchar dtyp;
362         long spfx;
363
364         if (!oart) return;
365
366         /* effects from the defn field */
367         dtyp = (wp_mask != W_ART) ? oart->defn.adtyp : oart->cary.adtyp;
368
369         if (dtyp == AD_FIRE)
370             mask = &EFire_resistance;
371         else if (dtyp == AD_COLD)
372             mask = &ECold_resistance;
373         else if (dtyp == AD_ELEC)
374             mask = &EShock_resistance;
375         else if (dtyp == AD_MAGM)
376             mask = &EAntimagic;
377         else if (dtyp == AD_DISN)
378             mask = &EDisint_resistance;
379         else if (dtyp == AD_DRST)
380             mask = &EPoison_resistance;
381
382         if (mask && wp_mask == W_ART && !on) {
383             /* find out if some other artifact also confers this intrinsic */
384             /* if so, leave the mask alone */
385             register struct obj* obj;
386             for(obj = invent; obj; obj = obj->nobj)
387                 if(obj != otmp && obj->oartifact) {
388                     register const struct artifact *art = get_artifact(obj);
389                     if(art->cary.adtyp == dtyp) {
390                         mask = (long *) 0;
391                         break;
392                     }
393                 }
394         }
395         if (mask) {
396             if (on) *mask |= wp_mask;
397             else *mask &= ~wp_mask;
398         }
399
400         /* intrinsics from the spfx field; there could be more than one */
401         spfx = (wp_mask != W_ART) ? oart->spfx : oart->cspfx;
402         if(spfx && wp_mask == W_ART && !on) {
403             /* don't change any spfx also conferred by other artifacts */
404             register struct obj* obj;
405             for(obj = invent; obj; obj = obj->nobj)
406                 if(obj != otmp && obj->oartifact) {
407                     register const struct artifact *art = get_artifact(obj);
408                     spfx &= ~art->cspfx;
409                 }
410         }
411
412         if (spfx & SPFX_SEARCH) {
413             if(on) ESearching |= wp_mask;
414             else ESearching &= ~wp_mask;
415         }
416         if (spfx & SPFX_HALRES) {
417             /* make_hallucinated must (re)set the mask itself to get
418              * the display right */
419             /* restoring needed because this is the only artifact intrinsic
420              * that can print a message--need to guard against being printed
421              * when restoring a game
422              */
423             (void) make_hallucinated((long)!on, restoring ? FALSE : TRUE, wp_mask);
424         }
425         if (spfx & SPFX_ESP) {
426             if(on) ETelepat |= wp_mask;
427             else ETelepat &= ~wp_mask;
428             see_monsters();
429         }
430         if (spfx & SPFX_STLTH) {
431             if (on) EStealth |= wp_mask;
432             else EStealth &= ~wp_mask;
433         }
434         if (spfx & SPFX_REGEN) {
435             if (on) ERegeneration |= wp_mask;
436             else ERegeneration &= ~wp_mask;
437         }
438         if (spfx & SPFX_TCTRL) {
439             if (on) ETeleport_control |= wp_mask;
440             else ETeleport_control &= ~wp_mask;
441         }
442         if (spfx & SPFX_WARN) {
443             if (spec_m2(otmp)) {
444                 if (on) {
445                         EWarn_of_mon |= wp_mask;
446                         flags.warntype |= spec_m2(otmp);
447                 } else {
448                         EWarn_of_mon &= ~wp_mask;
449                         flags.warntype &= ~spec_m2(otmp);
450                 }
451                 see_monsters();
452             } else {
453                 if (on) EWarning |= wp_mask;
454                 else EWarning &= ~wp_mask;
455             }
456         }
457         if (spfx & SPFX_EREGEN) {
458             if (on) EEnergy_regeneration |= wp_mask;
459             else EEnergy_regeneration &= ~wp_mask;
460         }
461         if (spfx & SPFX_HSPDAM) {
462             if (on) EHalf_spell_damage |= wp_mask;
463             else EHalf_spell_damage &= ~wp_mask;
464         }
465         if (spfx & SPFX_HPHDAM) {
466             if (on) EHalf_physical_damage |= wp_mask;
467             else EHalf_physical_damage &= ~wp_mask;
468         }
469         if (spfx & SPFX_XRAY) {
470             /* this assumes that no one else is using xray_range */
471             if (on) u.xray_range = 3;
472             else u.xray_range = -1;
473             vision_full_recalc = 1;
474         }
475         if ((spfx & SPFX_REFLECT) && (wp_mask & W_WEP)) {
476             if (on) EReflecting |= wp_mask;
477             else EReflecting &= ~wp_mask;
478         }
479
480         if(wp_mask == W_ART && !on && oart->inv_prop) {
481             /* might have to turn off invoked power too */
482             if (oart->inv_prop <= LAST_PROP &&
483                 (u.uprops[oart->inv_prop].extrinsic & W_ARTI))
484                 (void) arti_invoke(otmp);
485         }
486 }
487
488 /*
489  * creature (usually player) tries to touch (pick up or wield) an artifact obj.
490  * Returns 0 if the object refuses to be touched.
491  * This routine does not change any object chains.
492  * Ignores such things as gauntlets, assuming the artifact is not
493  * fooled by such trappings.
494  */
495 int
496 touch_artifact(obj,mon)
497     struct obj *obj;
498     struct monst *mon;
499 {
500     register const struct artifact *oart = get_artifact(obj);
501     boolean badclass, badalign, self_willed, yours;
502
503     if(!oart) return 1;
504
505     yours = (mon == &youmonst);
506     /* all quest artifacts are self-willed; it this ever changes, `badclass'
507        will have to be extended to explicitly include quest artifacts */
508     self_willed = ((oart->spfx & SPFX_INTEL) != 0);
509     if (yours) {
510         badclass = self_willed &&
511                    ((oart->role != NON_PM && !Role_if(oart->role)) ||
512                     (oart->race != NON_PM && !Race_if(oart->race)));
513         badalign = (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE &&
514                    (oart->alignment != u.ualign.type || u.ualign.record < 0);
515     } else if (!is_covetous(mon->data) && !is_mplayer(mon->data)) {
516         badclass = self_willed &&
517                    oart->role != NON_PM && oart != &artilist[ART_EXCALIBUR];
518         badalign = (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE &&
519                    (oart->alignment != sgn(mon->data->maligntyp));
520     } else {    /* an M3_WANTSxxx monster or a fake player */
521         /* special monsters trying to take the Amulet, invocation tools or
522            quest item can touch anything except for `spec_applies' artifacts */
523         badclass = badalign = FALSE;
524     }
525     /* weapons which attack specific categories of monsters are
526        bad for them even if their alignments happen to match */
527     if (!badalign && (oart->spfx & SPFX_DBONUS) != 0) {
528         struct artifact tmp;
529
530         tmp = *oart;
531         tmp.spfx &= SPFX_DBONUS;
532         badalign = !!spec_applies(&tmp, mon);
533     }
534
535     if (((badclass || badalign) && self_willed) ||
536        (badalign && (!yours || !rn2(4))))  {
537         int dmg;
538         char buf[BUFSZ];
539
540         if (!yours) return 0;
541         You("are blasted by %s power!", s_suffix(the(xname(obj))));
542         dmg = d((Antimagic ? 2 : 4), (self_willed ? 10 : 4));
543         Sprintf(buf, "touching %s", oart->name);
544         losehp(dmg, buf, KILLED_BY);
545         exercise(A_WIS, FALSE);
546     }
547
548     /* can pick it up unless you're totally non-synch'd with the artifact */
549     if (badclass && badalign && self_willed) {
550         if (yours) pline("%s your grasp!", Tobjnam(obj, "evade"));
551         return 0;
552     }
553
554     return 1;
555 }
556
557 #endif /* OVLB */
558 #ifdef OVL1
559
560 /* decide whether an artifact's special attacks apply against mtmp */
561 STATIC_OVL int
562 spec_applies(weap, mtmp)
563 register const struct artifact *weap;
564 struct monst *mtmp;
565 {
566         struct permonst *ptr;
567         boolean yours;
568
569         if(!(weap->spfx & (SPFX_DBONUS | SPFX_ATTK)))
570             return(weap->attk.adtyp == AD_PHYS);
571
572         yours = (mtmp == &youmonst);
573         ptr = mtmp->data;
574
575         if (weap->spfx & SPFX_DMONS) {
576             return (ptr == &mons[(int)weap->mtype]);
577         } else if (weap->spfx & SPFX_DCLAS) {
578             return (weap->mtype == (unsigned long)ptr->mlet);
579         } else if (weap->spfx & SPFX_DFLAG1) {
580             return ((ptr->mflags1 & weap->mtype) != 0L);
581         } else if (weap->spfx & SPFX_DFLAG2) {
582             return ((ptr->mflags2 & weap->mtype) || (yours &&
583                         ((!Upolyd && (urace.selfmask & weap->mtype)) ||
584                          ((weap->mtype & M2_WERE) && u.ulycn >= LOW_PM))));
585         } else if (weap->spfx & SPFX_DALIGN) {
586             return yours ? (u.ualign.type != weap->alignment) :
587                            (ptr->maligntyp == A_NONE ||
588                                 sgn(ptr->maligntyp) != weap->alignment);
589         } else if (weap->spfx & SPFX_ATTK) {
590             struct obj *defending_weapon = (yours ? uwep : MON_WEP(mtmp));
591
592             if (defending_weapon && defending_weapon->oartifact &&
593                     defends((int)weap->attk.adtyp, defending_weapon))
594                 return FALSE;
595             switch(weap->attk.adtyp) {
596                 case AD_FIRE:
597                         return !(yours ? Fire_resistance : resists_fire(mtmp));
598                 case AD_COLD:
599                         return !(yours ? Cold_resistance : resists_cold(mtmp));
600                 case AD_ELEC:
601                         return !(yours ? Shock_resistance : resists_elec(mtmp));
602                 case AD_MAGM:
603                 case AD_STUN:
604                         return !(yours ? Antimagic : (rn2(100) < ptr->mr));
605                 case AD_DRST:
606                         return !(yours ? Poison_resistance : resists_poison(mtmp));
607                 case AD_DRLI:
608                         return !(yours ? Drain_resistance : resists_drli(mtmp));
609                 case AD_STON:
610                         return !(yours ? Stone_resistance : resists_ston(mtmp));
611                 default:        impossible("Weird weapon special attack.");
612             }
613         }
614         return(0);
615 }
616
617 /* return the M2 flags of monster that an artifact's special attacks apply against */
618 long
619 spec_m2(otmp)
620 struct obj *otmp;
621 {
622         register const struct artifact *artifact = get_artifact(otmp);
623         if (artifact)
624                 return artifact->mtype;
625         return 0L;
626 }
627
628 /* special attack bonus */
629 int
630 spec_abon(otmp, mon)
631 struct obj *otmp;
632 struct monst *mon;
633 {
634         register const struct artifact *weap = get_artifact(otmp);
635
636         /* no need for an extra check for `NO_ATTK' because this will
637            always return 0 for any artifact which has that attribute */
638
639         if (weap && weap->attk.damn && spec_applies(weap, mon))
640             return rnd((int)weap->attk.damn);
641         return 0;
642 }
643
644 /* special damage bonus */
645 int
646 spec_dbon(otmp, mon, tmp)
647 struct obj *otmp;
648 struct monst *mon;
649 int tmp;
650 {
651         register const struct artifact *weap = get_artifact(otmp);
652
653         if (!weap || (weap->attk.adtyp == AD_PHYS && /* check for `NO_ATTK' */
654                         weap->attk.damn == 0 && weap->attk.damd == 0))
655             spec_dbon_applies = FALSE;
656         else
657             spec_dbon_applies = spec_applies(weap, mon);
658
659         if (spec_dbon_applies)
660             return weap->attk.damd ? rnd((int)weap->attk.damd) : max(tmp,1);
661         return 0;
662 }
663
664 /* add identified artifact to discoveries list */
665 void
666 discover_artifact(m)
667 xchar m;
668 {
669     int i;
670
671     /* look for this artifact in the discoveries list;
672        if we hit an empty slot then it's not present, so add it */
673     for (i = 0; i < NROFARTIFACTS; i++)
674         if (artidisco[i] == 0 || artidisco[i] == m) {
675             artidisco[i] = m;
676             return;
677         }
678     /* there is one slot per artifact, so we should never reach the
679        end without either finding the artifact or an empty slot... */
680     impossible("couldn't discover artifact (%d)", (int)m);
681 }
682
683 /* used to decide whether an artifact has been fully identified */
684 boolean
685 undiscovered_artifact(m)
686 xchar m;
687 {
688     int i;
689
690     /* look for this artifact in the discoveries list;
691        if we hit an empty slot then it's undiscovered */
692     for (i = 0; i < NROFARTIFACTS; i++)
693         if (artidisco[i] == m)
694             return FALSE;
695         else if (artidisco[i] == 0)
696             break;
697     return TRUE;
698 }
699
700 /* display a list of discovered artifacts; return their count */
701 int
702 disp_artifact_discoveries(tmpwin)
703 winid tmpwin;           /* supplied by dodiscover() */
704 {
705     int i, m, otyp;
706     char buf[BUFSZ];
707
708     for (i = 0; i < NROFARTIFACTS; i++) {
709         if (artidisco[i] == 0) break;   /* empty slot implies end of list */
710         if (i == 0) putstr(tmpwin, iflags.menu_headings, "Artifacts");
711         m = artidisco[i];
712         otyp = artilist[m].otyp;
713         Sprintf(buf, "  %s [%s %s]", artiname(m),
714                 align_str(artilist[m].alignment), simple_typename(otyp));
715         putstr(tmpwin, 0, buf);
716     }
717     return i;
718 }
719
720 #endif /* OVL1 */
721
722 #ifdef OVLB
723
724
725         /*
726          * Magicbane's intrinsic magic is incompatible with normal
727          * enchantment magic.  Thus, its effects have a negative
728          * dependence on spe.  Against low mr victims, it typically
729          * does "double athame" damage, 2d4.  Occasionally, it will
730          * cast unbalancing magic which effectively averages out to
731          * 4d4 damage (3d4 against high mr victims), for spe = 0.
732          *
733          * Prior to 3.4.1, the cancel (aka purge) effect always
734          * included the scare effect too; now it's one or the other.
735          * Likewise, the stun effect won't be combined with either
736          * of those two; it will be chosen separately or possibly
737          * used as a fallback when scare or cancel fails.
738          *
739          * [Historical note: a change to artifact_hit() for 3.4.0
740          * unintentionally made all of Magicbane's special effects
741          * be blocked if the defender successfully saved against a
742          * stun attack.  As of 3.4.1, those effects can occur but
743          * will be slightly less likely than they were in 3.3.x.]
744          */
745 #define MB_MAX_DIEROLL          8       /* rolls above this aren't magical */
746 static const char * const mb_verb[2][4] = {
747         { "probe", "stun", "scare", "cancel" },
748         { "prod", "amaze", "tickle", "purge" },
749 };
750 #define MB_INDEX_PROBE          0
751 #define MB_INDEX_STUN           1
752 #define MB_INDEX_SCARE          2
753 #define MB_INDEX_CANCEL         3
754
755 /* called when someone is being hit by Magicbane */
756 STATIC_OVL boolean
757 Mb_hit(magr, mdef, mb, dmgptr, dieroll, vis, hittee)
758 struct monst *magr, *mdef;      /* attacker and defender */
759 struct obj *mb;                 /* Magicbane */
760 int *dmgptr;                    /* extra damage target will suffer */
761 int dieroll;                    /* d20 that has already scored a hit */
762 boolean vis;                    /* whether the action can be seen */
763 char *hittee;                   /* target's name: "you" or mon_nam(mdef) */
764 {
765     struct permonst *old_uasmon;
766     const char *verb;
767     boolean youattack = (magr == &youmonst),
768             youdefend = (mdef == &youmonst),
769             resisted = FALSE, do_stun, do_confuse, result;
770     int attack_indx, scare_dieroll = MB_MAX_DIEROLL / 2;
771
772     result = FALSE;             /* no message given yet */
773     /* the most severe effects are less likely at higher enchantment */
774     if (mb->spe >= 3)
775         scare_dieroll /= (1 << (mb->spe / 3));
776     /* if target successfully resisted the artifact damage bonus,
777        reduce overall likelihood of the assorted special effects */
778     if (!spec_dbon_applies) dieroll += 1;
779
780     /* might stun even when attempting a more severe effect, but
781        in that case it will only happen if the other effect fails;
782        extra damage will apply regardless; 3.4.1: sometimes might
783        just probe even when it hasn't been enchanted */
784     do_stun = (max(mb->spe,0) < rn2(spec_dbon_applies ? 11 : 7));
785
786     /* the special effects also boost physical damage; increments are
787        generally cumulative, but since the stun effect is based on a
788        different criterium its damage might not be included; the base
789        damage is either 1d4 (athame) or 2d4 (athame+spec_dbon) depending
790        on target's resistance check against AD_STUN (handled by caller)
791        [note that a successful save against AD_STUN doesn't actually
792        prevent the target from ending up stunned] */
793     attack_indx = MB_INDEX_PROBE;
794     *dmgptr += rnd(4);                  /* (2..3)d4 */
795     if (do_stun) {
796         attack_indx = MB_INDEX_STUN;
797         *dmgptr += rnd(4);              /* (3..4)d4 */
798     }
799     if (dieroll <= scare_dieroll) {
800         attack_indx = MB_INDEX_SCARE;
801         *dmgptr += rnd(4);              /* (3..5)d4 */
802     }
803     if (dieroll <= (scare_dieroll / 2)) {
804         attack_indx = MB_INDEX_CANCEL;
805         *dmgptr += rnd(4);              /* (4..6)d4 */
806     }
807
808     /* give the hit message prior to inflicting the effects */
809     verb = mb_verb[!!Hallucination][attack_indx];
810     if (youattack || youdefend || vis) {
811         result = TRUE;
812         pline_The("magic-absorbing blade %s %s!",
813                   vtense((const char *)0, verb), hittee);
814         /* assume probing has some sort of noticeable feedback
815            even if it is being done by one monster to another */
816         if (attack_indx == MB_INDEX_PROBE && !canspotmon(mdef))
817             map_invisible(mdef->mx, mdef->my);
818     }
819
820     /* now perform special effects */
821     switch (attack_indx) {
822     case MB_INDEX_CANCEL:
823         old_uasmon = youmonst.data;
824         /* No mdef->mcan check: even a cancelled monster can be polymorphed
825          * into a golem, and the "cancel" effect acts as if some magical
826          * energy remains in spellcasting defenders to be absorbed later.
827          */
828         if (!cancel_monst(mdef, mb, youattack, FALSE, FALSE)) {
829             resisted = TRUE;
830         } else {
831             do_stun = FALSE;
832             if (youdefend) {
833                 if (youmonst.data != old_uasmon)
834                     *dmgptr = 0;    /* rehumanized, so no more damage */
835                 if (u.uenmax > 0) {
836                     You("lose magical energy!");
837                     u.uenmax--;
838                     if (u.uen > 0) u.uen--;
839                     flags.botl = 1;
840                 }
841             } else {
842                 if (mdef->data == &mons[PM_CLAY_GOLEM])
843                     mdef->mhp = 1;      /* cancelled clay golems will die */
844                 if (youattack && attacktype(mdef->data, AT_MAGC)) {
845                     You("absorb magical energy!");
846                     u.uenmax++;
847                     u.uen++;
848                     flags.botl = 1;
849                 }
850             }
851         }
852         break;
853
854     case MB_INDEX_SCARE:
855         if (youdefend) {
856             if (Antimagic) {
857                 resisted = TRUE;
858             } else {
859                 nomul(-3);
860                 nomovemsg = "";
861                 if (magr && magr == u.ustuck && sticks(youmonst.data)) {
862                     u.ustuck = (struct monst *)0;
863                     You("release %s!", mon_nam(magr));
864                 }
865             }
866         } else {
867             if (rn2(2) && resist(mdef, WEAPON_CLASS, 0, NOTELL))
868                 resisted = TRUE;
869             else
870                 monflee(mdef, 3, FALSE, (mdef->mhp > *dmgptr));
871         }
872         if (!resisted) do_stun = FALSE;
873         break;
874
875     case MB_INDEX_STUN:
876         do_stun = TRUE;         /* (this is redundant...) */
877         break;
878
879     case MB_INDEX_PROBE:
880         if (youattack && (mb->spe == 0 || !rn2(3 * abs(mb->spe)))) {
881             pline_The("%s is insightful.", verb);
882             /* pre-damage status */
883             probe_monster(mdef);
884         }
885         break;
886     }
887     /* stun if that was selected and a worse effect didn't occur */
888     if (do_stun) {
889         if (youdefend)
890             make_stunned((HStun + 3), FALSE);
891         else
892             mdef->mstun = 1;
893         /* avoid extra stun message below if we used mb_verb["stun"] above */
894         if (attack_indx == MB_INDEX_STUN) do_stun = FALSE;
895     }
896     /* lastly, all this magic can be confusing... */
897     do_confuse = !rn2(12);
898     if (do_confuse) {
899         if (youdefend)
900             make_confused(HConfusion + 4, FALSE);
901         else
902             mdef->mconf = 1;
903     }
904
905     if (youattack || youdefend || vis) {
906         (void) upstart(hittee); /* capitalize */
907         if (resisted) {
908             pline("%s %s!", hittee, vtense(hittee, "resist"));
909             shieldeff(youdefend ? u.ux : mdef->mx,
910                       youdefend ? u.uy : mdef->my);
911         }
912         if ((do_stun || do_confuse) && flags.verbose) {
913             char buf[BUFSZ];
914
915             buf[0] = '\0';
916             if (do_stun) Strcat(buf, "stunned");
917             if (do_stun && do_confuse) Strcat(buf, " and ");
918             if (do_confuse) Strcat(buf, "confused");
919             pline("%s %s %s%c", hittee, vtense(hittee, "are"),
920                   buf, (do_stun && do_confuse) ? '!' : '.');
921         }
922     }
923
924     return result;
925 }
926   
927 /* Function used when someone attacks someone else with an artifact
928  * weapon.  Only adds the special (artifact) damage, and returns a 1 if it
929  * did something special (in which case the caller won't print the normal
930  * hit message).  This should be called once upon every artifact attack;
931  * dmgval() no longer takes artifact bonuses into account.  Possible
932  * extension: change the killer so that when an orc kills you with
933  * Stormbringer it's "killed by Stormbringer" instead of "killed by an orc".
934  */
935 boolean
936 artifact_hit(magr, mdef, otmp, dmgptr, dieroll)
937 struct monst *magr, *mdef;
938 struct obj *otmp;
939 int *dmgptr;
940 int dieroll; /* needed for Magicbane and vorpal blades */
941 {
942         boolean youattack = (magr == &youmonst);
943         boolean youdefend = (mdef == &youmonst);
944         boolean vis = (!youattack && magr && cansee(magr->mx, magr->my))
945             || (!youdefend && cansee(mdef->mx, mdef->my))
946             || (youattack && u.uswallow && mdef == u.ustuck && !Blind);
947         boolean realizes_damage;
948         const char *wepdesc;
949         static const char you[] = "you";
950         char hittee[BUFSZ];
951
952         Strcpy(hittee, youdefend ? you : mon_nam(mdef));
953
954         /* The following takes care of most of the damage, but not all--
955          * the exception being for level draining, which is specially
956          * handled.  Messages are done in this function, however.
957          */
958         *dmgptr += spec_dbon(otmp, mdef, *dmgptr);
959
960         if (youattack && youdefend) {
961             impossible("attacking yourself with weapon?");
962             return FALSE;
963         }
964
965         realizes_damage = (youdefend || vis || 
966                            /* feel the effect even if not seen */
967                            (youattack && mdef == u.ustuck));
968
969         /* the four basic attacks: fire, cold, shock and missiles */
970         if (attacks(AD_FIRE, otmp)) {
971             if (realizes_damage)
972                 pline_The("fiery blade %s %s%c",
973                         !spec_dbon_applies ? "hits" :
974                         (mdef->data == &mons[PM_WATER_ELEMENTAL]) ?
975                         "vaporizes part of" : "burns",
976                         hittee, !spec_dbon_applies ? '.' : '!');
977             if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
978             if (!rn2(4)) (void) destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE);
979             if (!rn2(7)) (void) destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE);
980             if (youdefend && Slimed) burn_away_slime();
981             return realizes_damage;
982         }
983         if (attacks(AD_COLD, otmp)) {
984             if (realizes_damage)
985                 pline_The("ice-cold blade %s %s%c",
986                         !spec_dbon_applies ? "hits" : "freezes",
987                         hittee, !spec_dbon_applies ? '.' : '!');
988             if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_COLD);
989             return realizes_damage;
990         }
991         if (attacks(AD_ELEC, otmp)) {
992             if (realizes_damage)
993                 pline_The("massive hammer hits%s %s%c",
994                           !spec_dbon_applies ? "" : "!  Lightning strikes",
995                           hittee, !spec_dbon_applies ? '.' : '!');
996             if (!rn2(5)) (void) destroy_mitem(mdef, RING_CLASS, AD_ELEC);
997             if (!rn2(5)) (void) destroy_mitem(mdef, WAND_CLASS, AD_ELEC);
998             return realizes_damage;
999         }
1000         if (attacks(AD_MAGM, otmp)) {
1001             if (realizes_damage)
1002                 pline_The("imaginary widget hits%s %s%c",
1003                           !spec_dbon_applies ? "" :
1004                                 "!  A hail of magic missiles strikes",
1005                           hittee, !spec_dbon_applies ? '.' : '!');
1006             return realizes_damage;
1007         }
1008
1009         if (attacks(AD_STUN, otmp) && dieroll <= MB_MAX_DIEROLL) {
1010             /* Magicbane's special attacks (possibly modifies hittee[]) */
1011             return Mb_hit(magr, mdef, otmp, dmgptr, dieroll, vis, hittee);
1012         }
1013
1014         if (!spec_dbon_applies) {
1015             /* since damage bonus didn't apply, nothing more to do;  
1016                no further attacks have side-effects on inventory */
1017             return FALSE;
1018         }
1019
1020         /* We really want "on a natural 20" but Nethack does it in */
1021         /* reverse from AD&D. */
1022         if (spec_ability(otmp, SPFX_BEHEAD)) {
1023             if (otmp->oartifact == ART_TSURUGI_OF_MURAMASA && dieroll == 1) {
1024                 wepdesc = "The razor-sharp blade";
1025                 /* not really beheading, but so close, why add another SPFX */
1026                 if (youattack && u.uswallow && mdef == u.ustuck) {
1027                     You("slice %s wide open!", mon_nam(mdef));
1028                     *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1029                     return TRUE;
1030                 }
1031                 if (!youdefend) {
1032                         /* allow normal cutworm() call to add extra damage */
1033                         if(notonhead)
1034                             return FALSE;
1035
1036                         if (bigmonst(mdef->data)) {
1037                                 if (youattack)
1038                                         You("slice deeply into %s!",
1039                                                 mon_nam(mdef));
1040                                 else if (vis)
1041                                         pline("%s cuts deeply into %s!",
1042                                               Monnam(magr), hittee);
1043                                 *dmgptr *= 2;
1044                                 return TRUE;
1045                         }
1046                         *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1047                         pline("%s cuts %s in half!", wepdesc, mon_nam(mdef));
1048                         otmp->dknown = TRUE;
1049                         return TRUE;
1050                 } else {
1051                         if (bigmonst(youmonst.data)) {
1052                                 pline("%s cuts deeply into you!",
1053                                       magr ? Monnam(magr) : wepdesc);
1054                                 *dmgptr *= 2;
1055                                 return TRUE;
1056                         }
1057
1058                         /* Players with negative AC's take less damage instead
1059                          * of just not getting hit.  We must add a large enough
1060                          * value to the damage so that this reduction in
1061                          * damage does not prevent death.
1062                          */
1063                         *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
1064                         pline("%s cuts you in half!", wepdesc);
1065                         otmp->dknown = TRUE;
1066                         return TRUE;
1067                 }
1068             } else if (otmp->oartifact == ART_VORPAL_BLADE &&
1069                         (dieroll == 1 || mdef->data == &mons[PM_JABBERWOCK])) {
1070                 static const char * const behead_msg[2] = {
1071                      "%s beheads %s!",
1072                      "%s decapitates %s!"
1073                 };
1074
1075                 if (youattack && u.uswallow && mdef == u.ustuck)
1076                         return FALSE;
1077                 wepdesc = artilist[ART_VORPAL_BLADE].name;
1078                 if (!youdefend) {
1079                         if (!has_head(mdef->data) || notonhead || u.uswallow) {
1080                                 if (youattack)
1081                                         pline("Somehow, you miss %s wildly.",
1082                                                 mon_nam(mdef));
1083                                 else if (vis)
1084                                         pline("Somehow, %s misses wildly.",
1085                                                 mon_nam(magr));
1086                                 *dmgptr = 0;
1087                                 return ((boolean)(youattack || vis));
1088                         }
1089                         if (noncorporeal(mdef->data) || amorphous(mdef->data)) {
1090                                 pline("%s slices through %s %s.", wepdesc,
1091                                       s_suffix(mon_nam(mdef)),
1092                                       mbodypart(mdef,NECK));
1093                                 return TRUE;
1094                         }
1095                         *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1096                         pline(behead_msg[rn2(SIZE(behead_msg))],
1097                               wepdesc, mon_nam(mdef));
1098                         otmp->dknown = TRUE;
1099                         return TRUE;
1100                 } else {
1101                         if (!has_head(youmonst.data)) {
1102                                 pline("Somehow, %s misses you wildly.",
1103                                       magr ? mon_nam(magr) : wepdesc);
1104                                 *dmgptr = 0;
1105                                 return TRUE;
1106                         }
1107                         if (noncorporeal(youmonst.data) || amorphous(youmonst.data)) {
1108                                 pline("%s slices through your %s.",
1109                                       wepdesc, body_part(NECK));
1110                                 return TRUE;
1111                         }
1112                         *dmgptr = 2 * (Upolyd ? u.mh : u.uhp)
1113                                   + FATAL_DAMAGE_MODIFIER;
1114                         pline(behead_msg[rn2(SIZE(behead_msg))],
1115                               wepdesc, "you");
1116                         otmp->dknown = TRUE;
1117                         /* Should amulets fall off? */
1118                         return TRUE;
1119                 }
1120             }
1121         }
1122         if (spec_ability(otmp, SPFX_DRLI)) {
1123                 if (!youdefend) {
1124                         if (vis) {
1125                             if(otmp->oartifact == ART_STORMBRINGER)
1126                                 pline_The("%s blade draws the life from %s!",
1127                                       hcolor(NH_BLACK),
1128                                       mon_nam(mdef));
1129                             else
1130                                 pline("%s draws the life from %s!",
1131                                       The(distant_name(otmp, xname)),
1132                                       mon_nam(mdef));
1133                         }
1134                         if (mdef->m_lev == 0) {
1135                             *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
1136                         } else {
1137                             int drain = rnd(8);
1138                             *dmgptr += drain;
1139                             mdef->mhpmax -= drain;
1140                             mdef->m_lev--;
1141                             drain /= 2;
1142                             if (drain) healup(drain, 0, FALSE, FALSE);
1143                         }
1144                         return vis;
1145                 } else { /* youdefend */
1146                         int oldhpmax = u.uhpmax;
1147
1148                         if (Blind)
1149                                 You_feel("an %s drain your life!",
1150                                     otmp->oartifact == ART_STORMBRINGER ?
1151                                     "unholy blade" : "object");
1152                         else if (otmp->oartifact == ART_STORMBRINGER)
1153                                 pline_The("%s blade drains your life!",
1154                                       hcolor(NH_BLACK));
1155                         else
1156                                 pline("%s drains your life!",
1157                                       The(distant_name(otmp, xname)));
1158                         losexp("life drainage");
1159                         if (magr && magr->mhp < magr->mhpmax) {
1160                             magr->mhp += (oldhpmax - u.uhpmax)/2;
1161                             if (magr->mhp > magr->mhpmax) magr->mhp = magr->mhpmax;
1162                         }
1163                         return TRUE;
1164                 }
1165         }
1166         return FALSE;
1167 }
1168
1169 static NEARDATA const char recharge_type[] = { ALLOW_COUNT, ALL_CLASSES, 0 };
1170 static NEARDATA const char invoke_types[] = { ALL_CLASSES, 0 };
1171                 /* #invoke: an "ugly check" filters out most objects */
1172
1173 int
1174 doinvoke()
1175 {
1176     register struct obj *obj;
1177
1178     obj = getobj(invoke_types, "invoke");
1179     if (!obj) return 0;
1180     if (obj->oartifact && !touch_artifact(obj, &youmonst)) return 1;
1181     return arti_invoke(obj);
1182 }
1183
1184 STATIC_OVL int
1185 arti_invoke(obj)
1186     register struct obj *obj;
1187 {
1188     register const struct artifact *oart = get_artifact(obj);
1189
1190     if(!oart || !oart->inv_prop) {
1191         if(obj->otyp == CRYSTAL_BALL)
1192             use_crystal_ball(obj);
1193         else
1194             pline(nothing_happens);
1195         return 1;
1196     }
1197
1198     if(oart->inv_prop > LAST_PROP) {
1199         /* It's a special power, not "just" a property */
1200         if(obj->age > monstermoves) {
1201             /* the artifact is tired :-) */
1202             You_feel("that %s %s ignoring you.",
1203                      the(xname(obj)), otense(obj, "are"));
1204             /* and just got more so; patience is essential... */
1205             obj->age += (long) d(3,10);
1206             return 1;
1207         }
1208         obj->age = monstermoves + rnz(100);
1209
1210         switch(oart->inv_prop) {
1211         case TAMING: {
1212             struct obj pseudo;
1213
1214             pseudo = zeroobj;   /* neither cursed nor blessed */
1215             pseudo.otyp = SCR_TAMING;
1216             (void) seffects(&pseudo);
1217             break;
1218           }
1219         case HEALING: {
1220             int healamt = (u.uhpmax + 1 - u.uhp) / 2;
1221             long creamed = (long)u.ucreamed;
1222
1223             if (Upolyd) healamt = (u.mhmax + 1 - u.mh) / 2;
1224             if (healamt || Sick || Slimed || Blinded > creamed)
1225                 You_feel("better.");
1226             else
1227                 goto nothing_special;
1228             if (healamt > 0) {
1229                 if (Upolyd) u.mh += healamt;
1230                 else u.uhp += healamt;
1231             }
1232             if(Sick) make_sick(0L,(char *)0,FALSE,SICK_ALL);
1233             if(Slimed) Slimed = 0L;
1234             if (Blinded > creamed) make_blinded(creamed, FALSE);
1235             flags.botl = 1;
1236             break;
1237           }
1238         case ENERGY_BOOST: {
1239             int epboost = (u.uenmax + 1 - u.uen) / 2;
1240             if (epboost > 120) epboost = 120;           /* arbitrary */
1241             else if (epboost < 12) epboost = u.uenmax - u.uen;
1242             if(epboost) {
1243                 You_feel("re-energized.");
1244                 u.uen += epboost;
1245                 flags.botl = 1;
1246             } else
1247                 goto nothing_special;
1248             break;
1249           }
1250         case UNTRAP: {
1251             if(!untrap(TRUE)) {
1252                 obj->age = 0; /* don't charge for changing their mind */
1253                 return 0;
1254             }
1255             break;
1256           }
1257         case CHARGE_OBJ: {
1258             struct obj *otmp = getobj(recharge_type, "charge");
1259             boolean b_effect;
1260
1261             if (!otmp) {
1262                 obj->age = 0;
1263                 return 0;
1264             }
1265             b_effect = obj->blessed &&
1266                 (Role_switch == oart->role || !oart->role);
1267             recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0);
1268             update_inventory();
1269             break;
1270           }
1271         case LEV_TELE:
1272             level_tele();
1273             break;
1274         case CREATE_PORTAL: {
1275             int i, num_ok_dungeons, last_ok_dungeon = 0;
1276             d_level newlev;
1277             extern int n_dgns; /* from dungeon.c */
1278             winid tmpwin = create_nhwindow(NHW_MENU);
1279             anything any;
1280
1281             any.a_void = 0;     /* set all bits to zero */
1282             start_menu(tmpwin);
1283             /* use index+1 (cant use 0) as identifier */
1284             for (i = num_ok_dungeons = 0; i < n_dgns; i++) {
1285                 if (!dungeons[i].dunlev_ureached) continue;
1286                 any.a_int = i+1;
1287                 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1288                          dungeons[i].dname, MENU_UNSELECTED);
1289                 num_ok_dungeons++;
1290                 last_ok_dungeon = i;
1291             }
1292             end_menu(tmpwin, "Open a portal to which dungeon?");
1293             if (num_ok_dungeons > 1) {
1294                 /* more than one entry; display menu for choices */
1295                 menu_item *selected;
1296                 int n;
1297
1298                 n = select_menu(tmpwin, PICK_ONE, &selected);
1299                 if (n <= 0) {
1300                     destroy_nhwindow(tmpwin);
1301                     goto nothing_special;
1302                 }
1303                 i = selected[0].item.a_int - 1;
1304                 free((genericptr_t)selected);
1305             } else
1306                 i = last_ok_dungeon;    /* also first & only OK dungeon */
1307             destroy_nhwindow(tmpwin);
1308
1309             /*
1310              * i is now index into dungeon structure for the new dungeon.
1311              * Find the closest level in the given dungeon, open
1312              * a use-once portal to that dungeon and go there.
1313              * The closest level is either the entry or dunlev_ureached.
1314              */
1315             newlev.dnum = i;
1316             if(dungeons[i].depth_start >= depth(&u.uz))
1317                 newlev.dlevel = dungeons[i].entry_lev;
1318             else
1319                 newlev.dlevel = dungeons[i].dunlev_ureached;
1320             if(u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev) ||
1321                newlev.dnum == u.uz.dnum) {
1322                 You_feel("very disoriented for a moment.");
1323             } else {
1324                 if(!Blind) You("are surrounded by a shimmering sphere!");
1325                 else You_feel("weightless for a moment.");
1326                 goto_level(&newlev, FALSE, FALSE, FALSE);
1327             }
1328             break;
1329           }
1330         case ENLIGHTENING:
1331             enlightenment(0);
1332             break;
1333         case CREATE_AMMO: {
1334             struct obj *otmp = mksobj(ARROW, TRUE, FALSE);
1335
1336             if (!otmp) goto nothing_special;
1337             otmp->blessed = obj->blessed;
1338             otmp->cursed = obj->cursed;
1339             otmp->bknown = obj->bknown;
1340             if (obj->blessed) {
1341                 if (otmp->spe < 0) otmp->spe = 0;
1342                 otmp->quan += rnd(10);
1343             } else if (obj->cursed) {
1344                 if (otmp->spe > 0) otmp->spe = 0;
1345             } else
1346                 otmp->quan += rnd(5);
1347             otmp->owt = weight(otmp);
1348             otmp = hold_another_object(otmp, "Suddenly %s out.",
1349                                        aobjnam(otmp, "fall"), (const char *)0);
1350             break;
1351           }
1352         }
1353     } else {
1354         long eprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI),
1355              iprop = u.uprops[oart->inv_prop].intrinsic;
1356         boolean on = (eprop & W_ARTI) != 0; /* true if invoked prop just set */
1357
1358         if(on && obj->age > monstermoves) {
1359             /* the artifact is tired :-) */
1360             u.uprops[oart->inv_prop].extrinsic ^= W_ARTI;
1361             You_feel("that %s %s ignoring you.",
1362                      the(xname(obj)), otense(obj, "are"));
1363             /* can't just keep repeatedly trying */
1364             obj->age += (long) d(3,10);
1365             return 1;
1366         } else if(!on) {
1367             /* when turning off property, determine downtime */
1368             /* arbitrary for now until we can tune this -dlc */
1369             obj->age = monstermoves + rnz(100);
1370         }
1371
1372         if ((eprop & ~W_ARTI) || iprop) {
1373 nothing_special:
1374             /* you had the property from some other source too */
1375             if (carried(obj))
1376                 You_feel("a surge of power, but nothing seems to happen.");
1377             return 1;
1378         }
1379         switch(oart->inv_prop) {
1380         case CONFLICT:
1381             if(on) You_feel("like a rabble-rouser.");
1382             else You_feel("the tension decrease around you.");
1383             break;
1384         case LEVITATION:
1385             if(on) {
1386                 float_up();
1387                 spoteffects(FALSE);
1388             } else (void) float_down(I_SPECIAL|TIMEOUT, W_ARTI);
1389             break;
1390         case INVIS:
1391             if (BInvis || Blind) goto nothing_special;
1392             newsym(u.ux, u.uy);
1393             if (on)
1394                 Your("body takes on a %s transparency...",
1395                      Hallucination ? "normal" : "strange");
1396             else
1397                 Your("body seems to unfade...");
1398             break;
1399         }
1400     }
1401
1402     return 1;
1403 }
1404
1405
1406 /* WAC return TRUE if artifact is always lit */
1407 boolean
1408 artifact_light(obj)
1409     struct obj *obj;
1410 {
1411     return (get_artifact(obj) && obj->oartifact == ART_SUNSWORD);
1412 }
1413
1414 /* KMH -- Talking artifacts are finally implemented */
1415 void
1416 arti_speak(obj)
1417     struct obj *obj;
1418 {
1419         register const struct artifact *oart = get_artifact(obj);
1420         const char *line;
1421         char buf[BUFSZ];
1422
1423
1424         /* Is this a speaking artifact? */
1425         if (!oart || !(oart->spfx & SPFX_SPEAK))
1426                 return;
1427
1428         line = getrumor(bcsign(obj), buf, TRUE);
1429         if (!*line)
1430                 line = "NetHack rumors file closed for renovation.";
1431         pline("%s:", Tobjnam(obj, "whisper"));
1432         verbalize("%s", line);
1433         return;
1434 }
1435
1436 boolean
1437 artifact_has_invprop(otmp, inv_prop)
1438 struct obj *otmp;
1439 uchar inv_prop;
1440 {
1441         const struct artifact *arti = get_artifact(otmp);
1442
1443         return((boolean)(arti && (arti->inv_prop == inv_prop)));
1444 }
1445
1446 /* Return the price sold to the hero of a given artifact or unique item */
1447 long
1448 arti_cost(otmp)
1449 struct obj *otmp;
1450 {
1451         if (!otmp->oartifact)
1452             return ((long)objects[otmp->otyp].oc_cost);
1453         else if (artilist[(int) otmp->oartifact].cost)
1454             return (artilist[(int) otmp->oartifact].cost);
1455         else
1456             return (100L * (long)objects[otmp->otyp].oc_cost);
1457 }
1458
1459 #endif /* OVLB */
1460
1461 /*artifact.c*/