1 | /* SCCS Id: @(#)worn.c 3.3 2000/02/19 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 |
7 | STATIC_DCL void FDECL(m_lose_armor, (struct monst *,struct obj *));
8 | STATIC_DCL void FDECL(m_dowear_type, (struct monst *,long,BOOLEAN_P));
9 |
10 | const struct worn {
11 | long w_mask;
12 | struct obj **w_obj;
13 | } worn[] = {
14 | { W_ARM, &uarm },
15 | { W_ARMC, &uarmc },
16 | { W_ARMH, &uarmh },
17 | { W_ARMS, &uarms },
18 | { W_ARMG, &uarmg },
19 | { W_ARMF, &uarmf },
20 | #ifdef TOURIST
21 | { W_ARMU, &uarmu },
22 | #endif
23 | { W_RINGL, &uleft },
24 | { W_RINGR, &uright },
25 | { W_WEP, &uwep },
26 | { W_SWAPWEP, &uswapwep },
27 | { W_QUIVER, &uquiver },
28 | { W_AMUL, &uamul },
29 | { W_TOOL, &ublindf },
30 | { W_BALL, &uball },
31 | { W_CHAIN, &uchain },
32 | { 0, 0 }
33 | };
34 |
35 | /* This only allows for one blocking item per property */
36 | #define w_blocks(o,m) \
37 | ((o->otyp == MUMMY_WRAPPING && ((m) & W_ARMC)) ? INVIS : \
38 | (o->otyp == CORNUTHAUM && ((m) & W_ARMH) && \
39 | !Role_if(PM_WIZARD)) ? CLAIRVOYANT : 0)
40 | /* note: monsters don't have clairvoyance, so your role
41 | has no significant effect on their use of w_blocks() */
42 |
43 |
44 | /* Updated to use the extrinsic and blocked fields. */
45 | void
46 | setworn(obj, mask)
47 | register struct obj *obj;
48 | long mask;
49 | {
50 | register const struct worn *wp;
51 | register struct obj *oobj;
52 | register int p;
53 |
54 | if ((mask & (W_ARM|I_SPECIAL)) == (W_ARM|I_SPECIAL)) {
55 | /* restoring saved game; no properties are conferred via skin */
56 | uskin = obj;
57 | /* assert( !uarm ); */
58 | } else {
59 | for(wp = worn; wp->w_mask; wp++) if(wp->w_mask & mask) {
60 | oobj = *(wp->w_obj);
61 | if(oobj && !(oobj->owornmask & wp->w_mask))
62 | impossible("Setworn: mask = %ld.", wp->w_mask);
63 | if(oobj) {
64 | if (u.twoweap && (oobj->owornmask & (W_WEP|W_SWAPWEP)))
65 | u.twoweap = 0;
66 | oobj->owornmask &= ~wp->w_mask;
67 | if (wp->w_mask & ~(W_SWAPWEP|W_QUIVER)) {
68 | /* leave as "x = x <op> y", here and below, for broken
69 | * compilers */
70 | p = objects[oobj->otyp].oc_oprop;
71 | u.uprops[p].extrinsic =
72 | u.uprops[p].extrinsic & ~wp->w_mask;
73 | if ((p = w_blocks(oobj,mask)) != 0)
74 | u.uprops[p].blocked &= ~wp->w_mask;
75 | if (oobj->oartifact)
76 | set_artifact_intrinsic(oobj, 0, mask);
77 | }
78 | }
79 | *(wp->w_obj) = obj;
80 | if(obj) {
81 | obj->owornmask |= wp->w_mask;
82 | /* Prevent getting/blocking intrinsics from wielding
83 | * potions, through the quiver, etc.
84 | * Allow weapon-tools, too.
85 | * wp_mask should be same as mask at this point.
86 | */
87 | if (wp->w_mask & ~(W_SWAPWEP|W_QUIVER)) {
88 | if (obj->oclass == WEAPON_CLASS || is_weptool(obj) ||
89 | mask != W_WEP) {
90 | p = objects[obj->otyp].oc_oprop;
91 | u.uprops[p].extrinsic =
92 | u.uprops[p].extrinsic | wp->w_mask;
93 | if ((p = w_blocks(obj, mask)) != 0)
94 | u.uprops[p].blocked |= wp->w_mask;
95 | }
96 | if (obj->oartifact)
97 | set_artifact_intrinsic(obj, 1, mask);
98 | }
99 | }
100 | }
101 | }
102 | update_inventory();
103 | }
104 |
105 | /* called e.g. when obj is destroyed */
106 | /* Updated to use the extrinsic and blocked fields. */
107 | void
108 | setnotworn(obj)
109 | register struct obj *obj;
110 | {
111 | register const struct worn *wp;
112 | register int p;
113 |
114 | if (!obj) return;
115 | if (obj == uwep || obj == uswapwep) u.twoweap = 0;
116 | for(wp = worn; wp->w_mask; wp++)
117 | if(obj == *(wp->w_obj)) {
118 | *(wp->w_obj) = 0;
119 | p = objects[obj->otyp].oc_oprop;
120 | u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask;
121 | obj->owornmask &= ~wp->w_mask;
122 | if (obj->oartifact)
123 | set_artifact_intrinsic(obj, 0, wp->w_mask);
124 | if ((p = w_blocks(obj,wp->w_mask)) != 0)
125 | u.uprops[p].blocked &= ~wp->w_mask;
126 | }
127 | update_inventory();
128 | }
129 |
130 | void
131 | mon_set_minvis(mon)
132 | struct monst *mon;
133 | {
134 | mon->perminvis = 1;
135 | if (!mon->invis_blkd) {
136 | mon->minvis = 1;
137 | newsym(mon->mx, mon->my); /* make it disappear */
138 | if (mon->wormno) see_wsegs(mon); /* and any tail too */
139 | }
140 | }
141 |
142 | void
143 | mon_adjust_speed(mon, adjust)
144 | struct monst *mon;
145 | int adjust; /* positive => increase speed, negative => decrease */
146 | {
147 | struct obj *otmp;
148 |
149 | switch (adjust) {
150 | case 2:
151 | mon->permspeed = MFAST;
152 | break;
153 | case 1:
154 | if (mon->permspeed == MSLOW) mon->permspeed = 0;
155 | else mon->permspeed = MFAST;
156 | break;
157 | case 0: /* just check for worn speed boots */
158 | break;
159 | case -1:
160 | if (mon->permspeed == MFAST) mon->permspeed = 0;
161 | else mon->permspeed = MSLOW;
162 | break;
163 | case -2:
164 | mon->permspeed = MSLOW;
165 | break;
166 | }
167 |
168 | for (otmp = mon->minvent; otmp; otmp = otmp->nobj)
169 | if (otmp->owornmask && objects[otmp->otyp].oc_oprop == FAST)
170 | break;
171 | if (otmp) /* speed boots */
172 | mon->mspeed = MFAST;
173 | else
174 | mon->mspeed = mon->permspeed;
175 | }
176 |
177 | /* armor put on or taken off; might be magical variety */
178 | void
179 | update_mon_intrinsics(mon, obj, on)
180 | struct monst *mon;
181 | struct obj *obj;
182 | boolean on;
183 | {
184 | int unseen;
185 | uchar mask;
186 | struct obj *otmp;
187 | int which = (int) objects[obj->otyp].oc_oprop;
188 |
189 | unseen = !canseemon(mon);
190 | if (!which) goto maybe_blocks;
191 |
192 | if (on) {
193 | switch (which) {
194 | case INVIS:
195 | mon->minvis = !mon->invis_blkd;
196 | break;
197 | case FAST:
198 | mon_adjust_speed(mon, 0);
199 | break;
200 | /* properties handled elsewhere */
201 | case ANTIMAGIC:
202 | case REFLECTING:
203 | break;
204 | /* properties which have no effect for monsters */
205 | case CLAIRVOYANT:
206 | case STEALTH:
207 | case TELEPAT:
208 | break;
209 | /* properties which should have an effect but aren't implemented */
210 | case LEVITATION:
211 | case WWALKING:
212 | break;
213 | /* properties which maybe should have an effect but don't */
214 | case DISPLACED:
215 | case FUMBLING:
216 | case JUMPING:
217 | case PROTECTION:
218 | break;
219 | default:
220 | if (which <= 8) { /* 1 thru 8 correspond to MR_xxx mask values */
221 | /* FIRE,COLD,SLEEP,DISINT,SHOCK,POISON,ACID,STONE */
222 | mask = (uchar) (1 << (which - 1));
223 | mon->mintrinsics |= (unsigned short) mask;
224 | }
225 | break;
226 | }
227 | } else { /* off */
228 | switch (which) {
229 | case INVIS:
230 | mon->minvis = mon->perminvis;
231 | break;
232 | case FAST:
233 | mon_adjust_speed(mon, 0);
234 | break;
235 | case FIRE_RES:
236 | case COLD_RES:
237 | case SLEEP_RES:
238 | case DISINT_RES:
239 | case SHOCK_RES:
240 | case POISON_RES:
241 | case ACID_RES:
242 | case STONE_RES:
243 | mask = (uchar) (1 << (which - 1));
244 | /* If the monster doesn't have this resistance intrinsically,
245 | check whether any other worn item confers it. Note that
246 | we don't currently check for anything conferred via simply
247 | carrying an object. */
248 | if (!(mon->data->mresists & mask)) {
249 | for (otmp = mon->minvent; otmp; otmp = otmp->nobj)
250 | if (otmp->owornmask &&
251 | (int) objects[otmp->otyp].oc_oprop == which)
252 | break;
253 | if (!otmp)
254 | mon->mintrinsics &= ~((unsigned short) mask);
255 | }
256 | break;
257 | default:
258 | break;
259 | }
260 | }
261 |
262 | maybe_blocks:
263 | /* obj->owornmask has been cleared by this point, so we can't use it.
264 | However, since monsters don't wield armor, we don't have to guard
265 | against that and can get away with a blanket worn-mask value. */
266 | switch (w_blocks(obj,~0L)) {
267 | case INVIS:
268 | mon->invis_blkd = on ? 1 : 0;
269 | mon->minvis = on ? 0 : mon->perminvis;
270 | break;
271 | default:
272 | break;
273 | }
274 |
275 | #ifdef STEED
276 | if (!on && mon == u.usteed && obj->otyp == SADDLE)
277 | dismount_steed(DISMOUNT_FELL);
278 | #endif
279 |
280 | /* if couldn't see it but now can, or vice versa, update display */
281 | if (unseen ^ !canseemon(mon))
282 | newsym(mon->mx, mon->my);
283 | }
284 |
285 | int
286 | find_mac(mon)
287 | register struct monst *mon;
288 | {
289 | register struct obj *obj;
290 | int base = mon->data->ac;
291 | long mwflags = mon->misc_worn_check;
292 |
293 | for (obj = mon->minvent; obj; obj = obj->nobj) {
294 | if (obj->owornmask & mwflags)
295 | base -= ARM_BONUS(obj);
296 | /* since ARM_BONUS is positive, subtracting it increases AC */
297 | }
298 | return base;
299 | }
300 |
301 | /* weapons are handled separately; rings and eyewear aren't used by monsters */
302 |
303 | /* Wear the best object of each type that the monster has. During creation,
304 | * the monster can put everything on at once; otherwise, wearing takes time.
305 | * This doesn't affect monster searching for objects--a monster may very well
306 | * search for objects it would not want to wear, because we don't want to
307 | * check which_armor() each round.
308 | *
309 | * We'll let monsters put on shirts and/or suits under worn cloaks, but
310 | * not shirts under worn suits. This is somewhat arbitrary, but it's
311 | * too tedious to have them remove and later replace outer garments,
312 | * and preventing suits under cloaks makes it a little bit too easy for
313 | * players to influence what gets worn. Putting on a shirt underneath
314 | * already worn body armor is too obviously buggy...
315 | */
316 | void
317 | m_dowear(mon, creation)
318 | register struct monst *mon;
319 | boolean creation;
320 | {
321 | /* Note the restrictions here are the same as in dowear in do_wear.c
322 | * except for the additional restriction on intelligence. (Players
323 | * are always intelligent, even if polymorphed).
324 | */
325 | if (verysmall(mon->data) || nohands(mon->data) || is_animal(mon->data))
326 | return;
327 | /* give mummies a chance to wear their wrappings */
328 | if (mindless(mon->data) && (mon->data->mlet != S_MUMMY || !creation))
329 | return;
330 |
331 | m_dowear_type(mon, W_AMUL, creation);
332 | #ifdef TOURIST
333 | /* can't put on shirt if already wearing suit */
334 | if (!cantweararm(mon->data) || (mon->misc_worn_check & W_ARM))
335 | m_dowear_type(mon, W_ARMU, creation);
336 | #endif
337 | /* treating small as a special case allows
338 | hobbits, gnomes, and kobolds to wear cloaks */
339 | if (!cantweararm(mon->data) || mon->data->msize == MZ_SMALL)
340 | m_dowear_type(mon, W_ARMC, creation);
341 | m_dowear_type(mon, W_ARMH, creation);
342 | if (!MON_WEP(mon) || !bimanual(MON_WEP(mon)))
343 | m_dowear_type(mon, W_ARMS, creation);
344 | m_dowear_type(mon, W_ARMG, creation);
345 | if (!slithy(mon->data) && mon->data->mlet != S_CENTAUR)
346 | m_dowear_type(mon, W_ARMF, creation);
347 | if (!cantweararm(mon->data))
348 | m_dowear_type(mon, W_ARM, creation);
349 | }
350 |
351 | STATIC_OVL void
352 | m_dowear_type(mon, flag, creation)
353 | struct monst *mon;
354 | long flag;
355 | boolean creation;
356 | {
357 | struct obj *old, *best, *obj;
358 | int m_delay = 0;
359 |
360 | if (mon->mfrozen) return; /* probably putting previous item on */
361 |
362 | old = which_armor(mon, flag);
363 | if (old && old->cursed) return;
364 | if (old && flag == W_AMUL) return; /* no such thing as better amulets */
365 | best = old;
366 |
367 | for(obj = mon->minvent; obj; obj = obj->nobj) {
368 | switch(flag) {
369 | case W_AMUL:
370 | if (obj->oclass != AMULET_CLASS ||
371 | (obj->otyp != AMULET_OF_LIFE_SAVING &&
372 | obj->otyp != AMULET_OF_REFLECTION))
373 | continue;
374 | best = obj;
375 | goto outer_break; /* no such thing as better amulets */
376 | #ifdef TOURIST
377 | case W_ARMU:
378 | if (!is_shirt(obj)) continue;
379 | break;
380 | #endif
381 | case W_ARMC:
382 | if (!is_cloak(obj)) continue;
383 | break;
384 | case W_ARMH:
385 | if (!is_helmet(obj)) continue;
386 | break;
387 | case W_ARMS:
388 | if (!is_shield(obj)) continue;
389 | break;
390 | case W_ARMG:
391 | if (!is_gloves(obj)) continue;
392 | break;
393 | case W_ARMF:
394 | if (!is_boots(obj)) continue;
395 | break;
396 | case W_ARM:
397 | if (!is_suit(obj)) continue;
398 | break;
399 | }
400 | if (obj->owornmask) continue;
401 | /* I'd like to define a VISIBLE_ARM_BONUS which doesn't assume the
402 | * monster knows obj->spe, but if I did that, a monster would keep
403 | * switching forever between two -2 caps since when it took off one
404 | * it would forget spe and once again think the object is better
405 | * than what it already has.
406 | */
407 | if (best && (ARM_BONUS(best) >= ARM_BONUS(obj))) continue;
408 | best = obj;
409 | }
410 | outer_break:
411 | if (!best || best == old) return;
412 |
413 | /* if wearing a cloak, account for the time spent removing
414 | and re-wearing it when putting on a suit or shirt */
415 | if ((flag == W_ARM
416 | #ifdef TOURIST
417 | || flag == W_ARMU
418 | #endif
419 | ) && (mon->misc_worn_check & W_ARMC))
420 | m_delay += 2;
421 | /* when upgrading a piece of armor, account for time spent
422 | taking off current one */
423 | if (old)
424 | m_delay += objects[old->otyp].oc_delay;
425 |
426 | if (old) /* do this first to avoid "(being worn)" */
427 | old->owornmask = 0L;
428 | if (!creation) {
429 | if (canseemon(mon)) {
430 | char buf[BUFSZ];
431 |
432 | if (old)
433 | Sprintf(buf, " removes %s and", distant_name(old, doname));
434 | else
435 | buf[0] = '\0';
436 | pline("%s%s puts on %s.", Monnam(mon),
437 | buf, distant_name(best,doname));
438 | } /* can see it */
439 | m_delay += objects[best->otyp].oc_delay;
440 | mon->mfrozen = m_delay;
441 | if (mon->mfrozen) mon->mcanmove = 0;
442 | }
443 | if (old)
444 | update_mon_intrinsics(mon, old, FALSE);
445 | mon->misc_worn_check |= flag;
446 | best->owornmask |= flag;
447 | update_mon_intrinsics(mon, best, TRUE);
448 | }
449 |
450 | struct obj *
451 | which_armor(mon, flag)
452 | struct monst *mon;
453 | long flag;
454 | {
455 | register struct obj *obj;
456 |
457 | for(obj = mon->minvent; obj; obj = obj->nobj)
458 | if (obj->owornmask & flag) return obj;
459 | return((struct obj *)0);
460 | }
461 |
462 | /* remove an item of armor and then drop it */
463 | STATIC_OVL void
464 | m_lose_armor(mon, obj)
465 | struct monst *mon;
466 | struct obj *obj;
467 | {
468 | mon->misc_worn_check &= ~obj->owornmask;
469 | obj->owornmask = 0L;
470 | update_mon_intrinsics(mon, obj, FALSE);
471 |
472 | obj_extract_self(obj);
473 | place_object(obj, mon->mx, mon->my);
474 | /* call stackobj() if we ever drop anything that can merge */
475 | newsym(mon->mx, mon->my);
476 | }
477 |
478 | void
479 | mon_break_armor(mon)
480 | struct monst *mon;
481 | {
482 | register struct obj *otmp;
483 | struct permonst *mdat = mon->data;
484 | boolean vis = cansee(mon->mx, mon->my);
485 | const char *pronoun = him[pronoun_gender(mon)],
486 | *ppronoun = his[pronoun_gender(mon)];
487 |
488 | if (breakarm(mdat)) {
489 | if ((otmp = which_armor(mon, W_ARM)) != 0) {
490 | if (vis)
491 | pline("%s breaks out of %s armor!", Monnam(mon), ppronoun);
492 | else
493 | You_hear("a cracking sound.");
494 | m_useup(mon, otmp);
495 | }
496 | if ((otmp = which_armor(mon, W_ARMC)) != 0) {
497 | if (otmp->oartifact) {
498 | if (vis)
499 | pline("%s cloak falls off!", s_suffix(Monnam(mon)));
500 | m_lose_armor(mon, otmp);
501 | } else {
502 | if (vis)
503 | pline("%s cloak tears apart!", s_suffix(Monnam(mon)));
504 | else
505 | You_hear("a ripping sound.");
506 | m_useup(mon, otmp);
507 | }
508 | }
509 | #ifdef TOURIST
510 | if ((otmp = which_armor(mon, W_ARMU)) != 0) {
511 | if (vis)
512 | pline("%s shirt rips to shreds!", s_suffix(Monnam(mon)));
513 | else
514 | You_hear("a ripping sound.");
515 | m_useup(mon, otmp);
516 | }
517 | #endif
518 | } else if (sliparm(mdat)) {
519 | if ((otmp = which_armor(mon, W_ARM)) != 0) {
520 | if (vis)
521 | pline("%s armor falls around %s!",
522 | s_suffix(Monnam(mon)), pronoun);
523 | else
524 | You_hear("a thud.");
525 | m_lose_armor(mon, otmp);
526 | }
527 | if ((otmp = which_armor(mon, W_ARMC)) != 0) {
528 | if (vis) {
529 | if (is_whirly(mon->data))
530 | pline("%s cloak falls, unsupported!",
531 | s_suffix(Monnam(mon)));
532 | else
533 | pline("%s shrinks out of %s cloak!", Monnam(mon),
534 | ppronoun);
535 | }
536 | m_lose_armor(mon, otmp);
537 | }
538 | #ifdef TOURIST
539 | if ((otmp = which_armor(mon, W_ARMU)) != 0) {
540 | if (vis) {
541 | if (sliparm(mon->data))
542 | pline("%s seeps right through %s shirt!",
543 | Monnam(mon), ppronoun);
544 | else
545 | pline("%s becomes much too small for %s shirt!",
546 | Monnam(mon), ppronoun);
547 | }
548 | m_lose_armor(mon, otmp);
549 | }
550 | #endif
551 | }
552 | if (nohands(mdat) || verysmall(mdat)) {
553 | if ((otmp = which_armor(mon, W_ARMG)) != 0) {
554 | if (vis)
555 | pline("%s drops %s gloves%s!", Monnam(mon), ppronoun,
556 | MON_WEP(mon) ? " and weapon" : "");
557 | possibly_unwield(mon);
558 | m_lose_armor(mon, otmp);
559 | }
560 | if ((otmp = which_armor(mon, W_ARMS)) != 0) {
561 | if (vis)
562 | pline("%s can no longer hold %s shield!", Monnam(mon),
563 | ppronoun);
564 | else
565 | You_hear("a clank.");
566 | m_lose_armor(mon, otmp);
567 | }
568 | if ((otmp = which_armor(mon, W_ARMH)) != 0) {
569 | if (vis)
570 | pline("%s helmet falls to the %s!",
571 | s_suffix(Monnam(mon)), surface(mon->mx, mon->my));
572 | else
573 | You_hear("a clank.");
574 | m_lose_armor(mon, otmp);
575 | }
576 | }
577 | if (nohands(mdat) || verysmall(mdat) || slithy(mdat) ||
578 | mdat->mlet == S_CENTAUR) {
579 | if ((otmp = which_armor(mon, W_ARMF)) != 0) {
580 | if (vis) {
581 | if (is_whirly(mon->data))
582 | pline("%s boots fall away!",
583 | s_suffix(Monnam(mon)));
584 | else pline("%s boots %s off %s feet!",
585 | s_suffix(Monnam(mon)),
586 | verysmall(mdat) ? "slide" : "are pushed", ppronoun);
587 | }
588 | m_lose_armor(mon, otmp);
589 | }
590 | }
591 | #ifdef STEED
592 | if (!can_saddle(mon)) {
593 | if ((otmp = which_armor(mon, W_SADDLE)) != 0) {
594 | m_lose_armor(mon, otmp);
595 | if (vis)
596 | pline("%s saddle falls off.", s_suffix(Monnam(mon)));
597 | }
598 | if (mon == u.usteed)
599 | goto noride;
600 | } else if (mon == u.usteed && !can_ride(mon)) {
601 | noride:
602 | You("can no longer ride %s.", mon_nam(mon));
603 | if (touch_petrifies(u.usteed->data) &&
604 | !Stone_resistance && rnl(3)) {
605 | char buf[BUFSZ];
606 |
607 | You("touch %s.", mon_nam(u.usteed));
608 | Sprintf(buf, "falling off %s",
609 | an(u.usteed->data->mname));
610 | instapetrify(buf);
611 | }
612 | dismount_steed(DISMOUNT_FELL);
613 | }
614 | #endif
615 | return;
616 | }
617 |
618 | /*worn.c*/