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