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