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*/