1 /* NetHack 3.6 worn.c $NHDT-Date: 1550524569 2019/02/18 21:16:09 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.56 $ */
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. */
5
6 #include "hack.h"
7
8 STATIC_DCL void FDECL(m_lose_armor, (struct monst *, struct obj *));
9 STATIC_DCL void FDECL(m_dowear_type,
10 (struct monst *, long, BOOLEAN_P, BOOLEAN_P));
11 STATIC_DCL int FDECL(extra_pref, (struct monst *, struct obj *));
12
13 const struct worn {
14 long w_mask;
15 struct obj **w_obj;
16 } worn[] = { { W_ARM, &uarm },
17 { W_ARMC, &uarmc },
18 { W_ARMH, &uarmh },
19 { W_ARMS, &uarms },
20 { W_ARMG, &uarmg },
21 { W_ARMF, &uarmf },
22 { W_ARMU, &uarmu },
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)) \
38 ? INVIS \
39 : (o->otyp == CORNUTHAUM && ((m) & W_ARMH) && !Role_if(PM_WIZARD)) \
40 ? CLAIRVOYANT \
41 : 0)
42 /* note: monsters don't have clairvoyance, so your role
43 has no significant effect on their use of w_blocks() */
44
45 /* Updated to use the extrinsic and blocked fields. */
46 void
setworn(obj,mask)47 setworn(obj, mask)
48 register struct obj *obj;
49 long mask;
50 {
51 register const struct worn *wp;
52 register struct obj *oobj;
53 register int p;
54
55 if ((mask & (W_ARM | I_SPECIAL)) == (W_ARM | I_SPECIAL)) {
56 /* restoring saved game; no properties are conferred via skin */
57 uskin = obj;
58 /* assert( !uarm ); */
59 } else {
60 if ((mask & W_ARMOR))
61 u.uroleplay.nudist = FALSE;
62 for (wp = worn; wp->w_mask; wp++)
63 if (wp->w_mask & mask) {
64 oobj = *(wp->w_obj);
65 if (oobj && !(oobj->owornmask & wp->w_mask))
66 impossible("Setworn: mask = %ld.", wp->w_mask);
67 if (oobj) {
68 if (u.twoweap && (oobj->owornmask & (W_WEP | W_SWAPWEP)))
69 u.twoweap = 0;
70 oobj->owornmask &= ~wp->w_mask;
71 if (wp->w_mask & ~(W_SWAPWEP | W_QUIVER)) {
72 /* leave as "x = x <op> y", here and below, for broken
73 * compilers */
74 p = objects[oobj->otyp].oc_oprop;
75 u.uprops[p].extrinsic =
76 u.uprops[p].extrinsic & ~wp->w_mask;
77 if ((p = w_blocks(oobj, mask)) != 0)
78 u.uprops[p].blocked &= ~wp->w_mask;
79 if (oobj->oartifact)
80 set_artifact_intrinsic(oobj, 0, mask);
81 }
82 /* in case wearing or removal is in progress or removal
83 is pending (via 'A' command for multiple items) */
84 cancel_doff(oobj, wp->w_mask);
85 }
86 *(wp->w_obj) = obj;
87 if (obj) {
88 obj->owornmask |= wp->w_mask;
89 /* Prevent getting/blocking intrinsics from wielding
90 * potions, through the quiver, etc.
91 * Allow weapon-tools, too.
92 * wp_mask should be same as mask at this point.
93 */
94 if (wp->w_mask & ~(W_SWAPWEP | W_QUIVER)) {
95 if (obj->oclass == WEAPON_CLASS || is_weptool(obj)
96 || mask != W_WEP) {
97 p = objects[obj->otyp].oc_oprop;
98 u.uprops[p].extrinsic =
99 u.uprops[p].extrinsic | wp->w_mask;
100 if ((p = w_blocks(obj, mask)) != 0)
101 u.uprops[p].blocked |= wp->w_mask;
102 }
103 if (obj->oartifact)
104 set_artifact_intrinsic(obj, 1, mask);
105 }
106 }
107 }
108 }
109 update_inventory();
110 }
111
112 /* called e.g. when obj is destroyed */
113 /* Updated to use the extrinsic and blocked fields. */
114 void
setnotworn(obj)115 setnotworn(obj)
116 register struct obj *obj;
117 {
118 register const struct worn *wp;
119 register int p;
120
121 if (!obj)
122 return;
123 if (obj == uwep || obj == uswapwep)
124 u.twoweap = 0;
125 for (wp = worn; wp->w_mask; wp++)
126 if (obj == *(wp->w_obj)) {
127 /* in case wearing or removal is in progress or removal
128 is pending (via 'A' command for multiple items) */
129 cancel_doff(obj, wp->w_mask);
130
131 *(wp->w_obj) = 0;
132 p = objects[obj->otyp].oc_oprop;
133 u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask;
134 obj->owornmask &= ~wp->w_mask;
135 if (obj->oartifact)
136 set_artifact_intrinsic(obj, 0, wp->w_mask);
137 if ((p = w_blocks(obj, wp->w_mask)) != 0)
138 u.uprops[p].blocked &= ~wp->w_mask;
139 }
140 update_inventory();
141 }
142
143 /* return item worn in slot indiciated by wornmask; needed by poly_obj() */
144 struct obj *
wearmask_to_obj(wornmask)145 wearmask_to_obj(wornmask)
146 long wornmask;
147 {
148 const struct worn *wp;
149
150 for (wp = worn; wp->w_mask; wp++)
151 if (wp->w_mask & wornmask)
152 return *wp->w_obj;
153 return (struct obj *) 0;
154 }
155
156 /* return a bitmask of the equipment slot(s) a given item might be worn in */
157 long
wearslot(obj)158 wearslot(obj)
159 struct obj *obj;
160 {
161 int otyp = obj->otyp;
162 /* practically any item can be wielded or quivered; it's up to
163 our caller to handle such things--we assume "normal" usage */
164 long res = 0L; /* default: can't be worn anywhere */
165
166 switch (obj->oclass) {
167 case AMULET_CLASS:
168 res = W_AMUL; /* WORN_AMUL */
169 break;
170 case RING_CLASS:
171 res = W_RINGL | W_RINGR; /* W_RING, BOTH_SIDES */
172 break;
173 case ARMOR_CLASS:
174 switch (objects[otyp].oc_armcat) {
175 case ARM_SUIT:
176 res = W_ARM;
177 break; /* WORN_ARMOR */
178 case ARM_SHIELD:
179 res = W_ARMS;
180 break; /* WORN_SHIELD */
181 case ARM_HELM:
182 res = W_ARMH;
183 break; /* WORN_HELMET */
184 case ARM_GLOVES:
185 res = W_ARMG;
186 break; /* WORN_GLOVES */
187 case ARM_BOOTS:
188 res = W_ARMF;
189 break; /* WORN_BOOTS */
190 case ARM_CLOAK:
191 res = W_ARMC;
192 break; /* WORN_CLOAK */
193 case ARM_SHIRT:
194 res = W_ARMU;
195 break; /* WORN_SHIRT */
196 }
197 break;
198 case WEAPON_CLASS:
199 res = W_WEP | W_SWAPWEP;
200 if (objects[otyp].oc_merge)
201 res |= W_QUIVER;
202 break;
203 case TOOL_CLASS:
204 if (otyp == BLINDFOLD || otyp == TOWEL || otyp == LENSES)
205 res = W_TOOL; /* WORN_BLINDF */
206 else if (is_weptool(obj) || otyp == TIN_OPENER)
207 res = W_WEP | W_SWAPWEP;
208 else if (otyp == SADDLE)
209 res = W_SADDLE;
210 break;
211 case FOOD_CLASS:
212 if (obj->otyp == MEAT_RING)
213 res = W_RINGL | W_RINGR;
214 break;
215 case GEM_CLASS:
216 res = W_QUIVER;
217 break;
218 case BALL_CLASS:
219 res = W_BALL;
220 break;
221 case CHAIN_CLASS:
222 res = W_CHAIN;
223 break;
224 default:
225 break;
226 }
227 return res;
228 }
229
230 void
mon_set_minvis(mon)231 mon_set_minvis(mon)
232 struct monst *mon;
233 {
234 mon->perminvis = 1;
235 if (!mon->invis_blkd) {
236 mon->minvis = 1;
237 newsym(mon->mx, mon->my); /* make it disappear */
238 if (mon->wormno)
239 see_wsegs(mon); /* and any tail too */
240 }
241 }
242
243 void
mon_adjust_speed(mon,adjust,obj)244 mon_adjust_speed(mon, adjust, obj)
245 struct monst *mon;
246 int adjust; /* positive => increase speed, negative => decrease */
247 struct obj *obj; /* item to make known if effect can be seen */
248 {
249 struct obj *otmp;
250 boolean give_msg = !in_mklev, petrify = FALSE;
251 unsigned int oldspeed = mon->mspeed;
252
253 switch (adjust) {
254 case 2:
255 mon->permspeed = MFAST;
256 give_msg = FALSE; /* special case monster creation */
257 break;
258 case 1:
259 if (mon->permspeed == MSLOW)
260 mon->permspeed = 0;
261 else
262 mon->permspeed = MFAST;
263 break;
264 case 0: /* just check for worn speed boots */
265 break;
266 case -1:
267 if (mon->permspeed == MFAST)
268 mon->permspeed = 0;
269 else
270 mon->permspeed = MSLOW;
271 break;
272 case -2:
273 mon->permspeed = MSLOW;
274 give_msg = FALSE; /* (not currently used) */
275 break;
276 case -3: /* petrification */
277 /* take away intrinsic speed but don't reduce normal speed */
278 if (mon->permspeed == MFAST)
279 mon->permspeed = 0;
280 petrify = TRUE;
281 break;
282 case -4: /* green slime */
283 if (mon->permspeed == MFAST)
284 mon->permspeed = 0;
285 give_msg = FALSE;
286 break;
287 }
288
289 for (otmp = mon->minvent; otmp; otmp = otmp->nobj)
290 if (otmp->owornmask && objects[otmp->otyp].oc_oprop == FAST)
291 break;
292 if (otmp) /* speed boots */
293 mon->mspeed = MFAST;
294 else
295 mon->mspeed = mon->permspeed;
296
297 /* no message if monster is immobile (temp or perm) or unseen */
298 if (give_msg && (mon->mspeed != oldspeed || petrify) && mon->data->mmove
299 && !(mon->mfrozen || mon->msleeping) && canseemon(mon)) {
300 /* fast to slow (skipping intermediate state) or vice versa */
301 const char *howmuch =
302 (mon->mspeed + oldspeed == MFAST + MSLOW) ? "much " : "";
303
304 if (petrify) {
305 /* mimic the player's petrification countdown; "slowing down"
306 even if fast movement rate retained via worn speed boots */
307 if (flags.verbose)
308 pline("%s is slowing down.", Monnam(mon));
309 } else if (adjust > 0 || mon->mspeed == MFAST)
310 pline("%s is suddenly moving %sfaster.", Monnam(mon), howmuch);
311 else
312 pline("%s seems to be moving %sslower.", Monnam(mon), howmuch);
313
314 /* might discover an object if we see the speed change happen */
315 if (obj != 0)
316 learnwand(obj);
317 }
318 }
319
320 /* armor put on or taken off; might be magical variety
321 [TODO: rename to 'update_mon_extrinsics()' and change all callers...] */
322 void
update_mon_intrinsics(mon,obj,on,silently)323 update_mon_intrinsics(mon, obj, on, silently)
324 struct monst *mon;
325 struct obj *obj;
326 boolean on, silently;
327 {
328 int unseen;
329 uchar mask;
330 struct obj *otmp;
331 int which = (int) objects[obj->otyp].oc_oprop;
332
333 unseen = !canseemon(mon);
334 if (!which)
335 goto maybe_blocks;
336
337 if (on) {
338 switch (which) {
339 case INVIS:
340 mon->minvis = !mon->invis_blkd;
341 break;
342 case FAST: {
343 boolean save_in_mklev = in_mklev;
344 if (silently)
345 in_mklev = TRUE;
346 mon_adjust_speed(mon, 0, obj);
347 in_mklev = save_in_mklev;
348 break;
349 }
350 /* properties handled elsewhere */
351 case ANTIMAGIC:
352 case REFLECTING:
353 break;
354 /* properties which have no effect for monsters */
355 case CLAIRVOYANT:
356 case STEALTH:
357 case TELEPAT:
358 break;
359 /* properties which should have an effect but aren't implemented */
360 case LEVITATION:
361 case WWALKING:
362 break;
363 /* properties which maybe should have an effect but don't */
364 case DISPLACED:
365 case FUMBLING:
366 case JUMPING:
367 case PROTECTION:
368 break;
369 default:
370 if (which <= 8) { /* 1 thru 8 correspond to MR_xxx mask values */
371 /* FIRE,COLD,SLEEP,DISINT,SHOCK,POISON,ACID,STONE */
372 mask = (uchar) (1 << (which - 1));
373 mon->mextrinsics |= (unsigned short) mask;
374 }
375 break;
376 }
377 } else { /* off */
378 switch (which) {
379 case INVIS:
380 mon->minvis = mon->perminvis;
381 break;
382 case FAST: {
383 boolean save_in_mklev = in_mklev;
384 if (silently)
385 in_mklev = TRUE;
386 mon_adjust_speed(mon, 0, obj);
387 in_mklev = save_in_mklev;
388 break;
389 }
390 case FIRE_RES:
391 case COLD_RES:
392 case SLEEP_RES:
393 case DISINT_RES:
394 case SHOCK_RES:
395 case POISON_RES:
396 case ACID_RES:
397 case STONE_RES:
398 mask = (uchar) (1 << (which - 1));
399 /* update monster's extrinsics (for worn objects only;
400 'obj' itself might still be worn or already unworn) */
401 for (otmp = mon->minvent; otmp; otmp = otmp->nobj)
402 if (otmp != obj
403 && otmp->owornmask
404 && (int) objects[otmp->otyp].oc_oprop == which)
405 break;
406 if (!otmp)
407 mon->mextrinsics &= ~((unsigned short) mask);
408 break;
409 default:
410 break;
411 }
412 }
413
414 maybe_blocks:
415 /* obj->owornmask has been cleared by this point, so we can't use it.
416 However, since monsters don't wield armor, we don't have to guard
417 against that and can get away with a blanket worn-mask value. */
418 switch (w_blocks(obj, ~0L)) {
419 case INVIS:
420 mon->invis_blkd = on ? 1 : 0;
421 mon->minvis = on ? 0 : mon->perminvis;
422 break;
423 default:
424 break;
425 }
426
427 if (!on && mon == u.usteed && obj->otyp == SADDLE)
428 dismount_steed(DISMOUNT_FELL);
429
430 /* if couldn't see it but now can, or vice versa, update display */
431 if (!silently && (unseen ^ !canseemon(mon)))
432 newsym(mon->mx, mon->my);
433 }
434
435 int
find_mac(mon)436 find_mac(mon)
437 register struct monst *mon;
438 {
439 register struct obj *obj;
440 int base = mon->data->ac;
441 long mwflags = mon->misc_worn_check;
442
443 for (obj = mon->minvent; obj; obj = obj->nobj) {
444 if (obj->owornmask & mwflags)
445 base -= ARM_BONUS(obj);
446 /* since ARM_BONUS is positive, subtracting it increases AC */
447 }
448 return base;
449 }
450
451 /*
452 * weapons are handled separately;
453 * rings and eyewear aren't used by monsters
454 */
455
456 /* Wear the best object of each type that the monster has. During creation,
457 * the monster can put everything on at once; otherwise, wearing takes time.
458 * This doesn't affect monster searching for objects--a monster may very well
459 * search for objects it would not want to wear, because we don't want to
460 * check which_armor() each round.
461 *
462 * We'll let monsters put on shirts and/or suits under worn cloaks, but
463 * not shirts under worn suits. This is somewhat arbitrary, but it's
464 * too tedious to have them remove and later replace outer garments,
465 * and preventing suits under cloaks makes it a little bit too easy for
466 * players to influence what gets worn. Putting on a shirt underneath
467 * already worn body armor is too obviously buggy...
468 */
469 void
m_dowear(mon,creation)470 m_dowear(mon, creation)
471 register struct monst *mon;
472 boolean creation;
473 {
474 #define RACE_EXCEPTION TRUE
475 /* Note the restrictions here are the same as in dowear in do_wear.c
476 * except for the additional restriction on intelligence. (Players
477 * are always intelligent, even if polymorphed).
478 */
479 if (verysmall(mon->data) || nohands(mon->data) || is_animal(mon->data))
480 return;
481 /* give mummies a chance to wear their wrappings
482 * and let skeletons wear their initial armor */
483 if (mindless(mon->data)
484 && (!creation || (mon->data->mlet != S_MUMMY
485 && mon->data != &mons[PM_SKELETON])))
486 return;
487
488 m_dowear_type(mon, W_AMUL, creation, FALSE);
489 /* can't put on shirt if already wearing suit */
490 if (!cantweararm(mon->data) && !(mon->misc_worn_check & W_ARM))
491 m_dowear_type(mon, W_ARMU, creation, FALSE);
492 /* treating small as a special case allows
493 hobbits, gnomes, and kobolds to wear cloaks */
494 if (!cantweararm(mon->data) || mon->data->msize == MZ_SMALL)
495 m_dowear_type(mon, W_ARMC, creation, FALSE);
496 m_dowear_type(mon, W_ARMH, creation, FALSE);
497 if (!MON_WEP(mon) || !bimanual(MON_WEP(mon)))
498 m_dowear_type(mon, W_ARMS, creation, FALSE);
499 m_dowear_type(mon, W_ARMG, creation, FALSE);
500 if (!slithy(mon->data) && mon->data->mlet != S_CENTAUR)
501 m_dowear_type(mon, W_ARMF, creation, FALSE);
502 if (!cantweararm(mon->data))
503 m_dowear_type(mon, W_ARM, creation, FALSE);
504 else
505 m_dowear_type(mon, W_ARM, creation, RACE_EXCEPTION);
506 }
507
508 STATIC_OVL void
m_dowear_type(mon,flag,creation,racialexception)509 m_dowear_type(mon, flag, creation, racialexception)
510 struct monst *mon;
511 long flag;
512 boolean creation;
513 boolean racialexception;
514 {
515 struct obj *old, *best, *obj;
516 int m_delay = 0;
517 int unseen = !canseemon(mon);
518 boolean autocurse;
519 char nambuf[BUFSZ];
520
521 if (mon->mfrozen)
522 return; /* probably putting previous item on */
523
524 /* Get a copy of monster's name before altering its visibility */
525 Strcpy(nambuf, See_invisible ? Monnam(mon) : mon_nam(mon));
526
527 old = which_armor(mon, flag);
528 if (old && old->cursed)
529 return;
530 if (old && flag == W_AMUL)
531 return; /* no such thing as better amulets */
532 best = old;
533
534 for (obj = mon->minvent; obj; obj = obj->nobj) {
535 switch (flag) {
536 case W_AMUL:
537 if (obj->oclass != AMULET_CLASS
538 || (obj->otyp != AMULET_OF_LIFE_SAVING
539 && obj->otyp != AMULET_OF_REFLECTION))
540 continue;
541 best = obj;
542 goto outer_break; /* no such thing as better amulets */
543 case W_ARMU:
544 if (!is_shirt(obj))
545 continue;
546 break;
547 case W_ARMC:
548 if (!is_cloak(obj))
549 continue;
550 break;
551 case W_ARMH:
552 if (!is_helmet(obj))
553 continue;
554 /* changing alignment is not implemented for monsters;
555 priests and minions could change alignment but wouldn't
556 want to, so they reject helms of opposite alignment */
557 if (obj->otyp == HELM_OF_OPPOSITE_ALIGNMENT
558 && (mon->ispriest || mon->isminion))
559 continue;
560 /* (flimsy exception matches polyself handling) */
561 if (has_horns(mon->data) && !is_flimsy(obj))
562 continue;
563 break;
564 case W_ARMS:
565 if (!is_shield(obj))
566 continue;
567 break;
568 case W_ARMG:
569 if (!is_gloves(obj))
570 continue;
571 break;
572 case W_ARMF:
573 if (!is_boots(obj))
574 continue;
575 break;
576 case W_ARM:
577 if (!is_suit(obj))
578 continue;
579 if (racialexception && (racial_exception(mon, obj) < 1))
580 continue;
581 break;
582 }
583 if (obj->owornmask)
584 continue;
585 /* I'd like to define a VISIBLE_ARM_BONUS which doesn't assume the
586 * monster knows obj->spe, but if I did that, a monster would keep
587 * switching forever between two -2 caps since when it took off one
588 * it would forget spe and once again think the object is better
589 * than what it already has.
590 */
591 if (best && (ARM_BONUS(best) + extra_pref(mon, best)
592 >= ARM_BONUS(obj) + extra_pref(mon, obj)))
593 continue;
594 best = obj;
595 }
596 outer_break:
597 if (!best || best == old)
598 return;
599
600 /* same auto-cursing behavior as for hero */
601 autocurse = ((best->otyp == HELM_OF_OPPOSITE_ALIGNMENT
602 || best->otyp == DUNCE_CAP) && !best->cursed);
603 /* if wearing a cloak, account for the time spent removing
604 and re-wearing it when putting on a suit or shirt */
605 if ((flag == W_ARM || flag == W_ARMU) && (mon->misc_worn_check & W_ARMC))
606 m_delay += 2;
607 /* when upgrading a piece of armor, account for time spent
608 taking off current one */
609 if (old)
610 m_delay += objects[old->otyp].oc_delay;
611
612 if (old) /* do this first to avoid "(being worn)" */
613 old->owornmask = 0L;
614 if (!creation) {
615 if (canseemon(mon)) {
616 char buf[BUFSZ];
617
618 if (old)
619 Sprintf(buf, " removes %s and", distant_name(old, doname));
620 else
621 buf[0] = '\0';
622 pline("%s%s puts on %s.", Monnam(mon), buf,
623 distant_name(best, doname));
624 if (autocurse)
625 pline("%s %s %s %s for a moment.", s_suffix(Monnam(mon)),
626 simpleonames(best), otense(best, "glow"),
627 hcolor(NH_BLACK));
628 } /* can see it */
629 m_delay += objects[best->otyp].oc_delay;
630 mon->mfrozen = m_delay;
631 if (mon->mfrozen)
632 mon->mcanmove = 0;
633 }
634 if (old)
635 update_mon_intrinsics(mon, old, FALSE, creation);
636 mon->misc_worn_check |= flag;
637 best->owornmask |= flag;
638 if (autocurse)
639 curse(best);
640 update_mon_intrinsics(mon, best, TRUE, creation);
641 /* if couldn't see it but now can, or vice versa, */
642 if (!creation && (unseen ^ !canseemon(mon))) {
643 if (mon->minvis && !See_invisible) {
644 pline("Suddenly you cannot see %s.", nambuf);
645 makeknown(best->otyp);
646 } /* else if (!mon->minvis) pline("%s suddenly appears!",
647 Amonnam(mon)); */
648 }
649 }
650 #undef RACE_EXCEPTION
651
652 struct obj *
which_armor(mon,flag)653 which_armor(mon, flag)
654 struct monst *mon;
655 long flag;
656 {
657 if (mon == &youmonst) {
658 switch (flag) {
659 case W_ARM:
660 return uarm;
661 case W_ARMC:
662 return uarmc;
663 case W_ARMH:
664 return uarmh;
665 case W_ARMS:
666 return uarms;
667 case W_ARMG:
668 return uarmg;
669 case W_ARMF:
670 return uarmf;
671 case W_ARMU:
672 return uarmu;
673 default:
674 impossible("bad flag in which_armor");
675 return 0;
676 }
677 } else {
678 register struct obj *obj;
679
680 for (obj = mon->minvent; obj; obj = obj->nobj)
681 if (obj->owornmask & flag)
682 return obj;
683 return (struct obj *) 0;
684 }
685 }
686
687 /* remove an item of armor and then drop it */
688 STATIC_OVL void
m_lose_armor(mon,obj)689 m_lose_armor(mon, obj)
690 struct monst *mon;
691 struct obj *obj;
692 {
693 mon->misc_worn_check &= ~obj->owornmask;
694 if (obj->owornmask)
695 update_mon_intrinsics(mon, obj, FALSE, FALSE);
696 obj->owornmask = 0L;
697
698 obj_extract_self(obj);
699 place_object(obj, mon->mx, mon->my);
700 /* call stackobj() if we ever drop anything that can merge */
701 newsym(mon->mx, mon->my);
702 }
703
704 /* all objects with their bypass bit set should now be reset to normal */
705 void
clear_bypasses()706 clear_bypasses()
707 {
708 struct obj *otmp, *nobj;
709 struct monst *mtmp;
710
711 /*
712 * 'Object' bypass is also used for one monster function:
713 * polymorph control of long worms. Activated via setting
714 * context.bypasses even if no specific object has been
715 * bypassed.
716 */
717
718 for (otmp = fobj; otmp; otmp = nobj) {
719 nobj = otmp->nobj;
720 if (otmp->bypass) {
721 otmp->bypass = 0;
722
723 /* bypass will have inhibited any stacking, but since it's
724 * used for polymorph handling, the objects here probably
725 * have been transformed and won't be stacked in the usual
726 * manner afterwards; so don't bother with this.
727 * [Changing the fobj chain mid-traversal would also be risky.]
728 */
729 #if 0
730 if (objects[otmp->otyp].oc_merge) {
731 xchar ox, oy;
732
733 (void) get_obj_location(otmp, &ox, &oy, 0);
734 stack_object(otmp);
735 newsym(ox, oy);
736 }
737 #endif /*0*/
738 }
739 }
740 for (otmp = invent; otmp; otmp = otmp->nobj)
741 otmp->bypass = 0;
742 for (otmp = migrating_objs; otmp; otmp = otmp->nobj)
743 otmp->bypass = 0;
744 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
745 if (DEADMONSTER(mtmp))
746 continue;
747 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
748 otmp->bypass = 0;
749 /* long worm created by polymorph has mon->mextra->mcorpsenm set
750 to PM_LONG_WORM to flag it as not being subject to further
751 polymorph (so polymorph zap won't hit monster to transform it
752 into a long worm, then hit that worm's tail and transform it
753 again on same zap); clearing mcorpsenm reverts worm to normal */
754 if (mtmp->data == &mons[PM_LONG_WORM] && has_mcorpsenm(mtmp))
755 MCORPSENM(mtmp) = NON_PM;
756 }
757 for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) {
758 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
759 otmp->bypass = 0;
760 /* no MCORPSENM(mtmp)==PM_LONG_WORM check here; long worms can't
761 be just created by polymorph and migrating at the same time */
762 }
763 /* billobjs and mydogs chains don't matter here */
764 context.bypasses = FALSE;
765 }
766
767 void
bypass_obj(obj)768 bypass_obj(obj)
769 struct obj *obj;
770 {
771 obj->bypass = 1;
772 context.bypasses = TRUE;
773 }
774
775 /* set or clear the bypass bit in a list of objects */
776 void
bypass_objlist(objchain,on)777 bypass_objlist(objchain, on)
778 struct obj *objchain;
779 boolean on; /* TRUE => set, FALSE => clear */
780 {
781 if (on && objchain)
782 context.bypasses = TRUE;
783 while (objchain) {
784 objchain->bypass = on ? 1 : 0;
785 objchain = objchain->nobj;
786 }
787 }
788
789 /* return the first object without its bypass bit set; set that bit
790 before returning so that successive calls will find further objects */
791 struct obj *
nxt_unbypassed_obj(objchain)792 nxt_unbypassed_obj(objchain)
793 struct obj *objchain;
794 {
795 while (objchain) {
796 if (!objchain->bypass) {
797 bypass_obj(objchain);
798 break;
799 }
800 objchain = objchain->nobj;
801 }
802 return objchain;
803 }
804
805 /* like nxt_unbypassed_obj() but operates on sortloot_item array rather
806 than an object linked list; the array contains obj==Null terminator;
807 there's an added complication that the array may have stale pointers
808 for deleted objects (see Multiple-Drop case in askchain(invent.c)) */
809 struct obj *
nxt_unbypassed_loot(lootarray,listhead)810 nxt_unbypassed_loot(lootarray, listhead)
811 Loot *lootarray;
812 struct obj *listhead;
813 {
814 struct obj *o, *obj;
815
816 while ((obj = lootarray->obj) != 0) {
817 for (o = listhead; o; o = o->nobj)
818 if (o == obj)
819 break;
820 if (o && !obj->bypass) {
821 bypass_obj(obj);
822 break;
823 }
824 ++lootarray;
825 }
826 return obj;
827 }
828
829 void
mon_break_armor(mon,polyspot)830 mon_break_armor(mon, polyspot)
831 struct monst *mon;
832 boolean polyspot;
833 {
834 register struct obj *otmp;
835 struct permonst *mdat = mon->data;
836 boolean vis = cansee(mon->mx, mon->my);
837 boolean handless_or_tiny = (nohands(mdat) || verysmall(mdat));
838 const char *pronoun = mhim(mon), *ppronoun = mhis(mon);
839
840 if (breakarm(mdat)) {
841 if ((otmp = which_armor(mon, W_ARM)) != 0) {
842 if ((Is_dragon_scales(otmp) && mdat == Dragon_scales_to_pm(otmp))
843 || (Is_dragon_mail(otmp) && mdat == Dragon_mail_to_pm(otmp)))
844 ; /* no message here;
845 "the dragon merges with his scaly armor" is odd
846 and the monster's previous form is already gone */
847 else if (vis)
848 pline("%s breaks out of %s armor!", Monnam(mon), ppronoun);
849 else
850 You_hear("a cracking sound.");
851 m_useup(mon, otmp);
852 }
853 if ((otmp = which_armor(mon, W_ARMC)) != 0) {
854 if (otmp->oartifact) {
855 if (vis)
856 pline("%s %s falls off!", s_suffix(Monnam(mon)),
857 cloak_simple_name(otmp));
858 if (polyspot)
859 bypass_obj(otmp);
860 m_lose_armor(mon, otmp);
861 } else {
862 if (vis)
863 pline("%s %s tears apart!", s_suffix(Monnam(mon)),
864 cloak_simple_name(otmp));
865 else
866 You_hear("a ripping sound.");
867 m_useup(mon, otmp);
868 }
869 }
870 if ((otmp = which_armor(mon, W_ARMU)) != 0) {
871 if (vis)
872 pline("%s shirt rips to shreds!", s_suffix(Monnam(mon)));
873 else
874 You_hear("a ripping sound.");
875 m_useup(mon, otmp);
876 }
877 } else if (sliparm(mdat)) {
878 if ((otmp = which_armor(mon, W_ARM)) != 0) {
879 if (vis)
880 pline("%s armor falls around %s!", s_suffix(Monnam(mon)),
881 pronoun);
882 else
883 You_hear("a thud.");
884 if (polyspot)
885 bypass_obj(otmp);
886 m_lose_armor(mon, otmp);
887 }
888 if ((otmp = which_armor(mon, W_ARMC)) != 0) {
889 if (vis) {
890 if (is_whirly(mon->data))
891 pline("%s %s falls, unsupported!", s_suffix(Monnam(mon)),
892 cloak_simple_name(otmp));
893 else
894 pline("%s shrinks out of %s %s!", Monnam(mon), ppronoun,
895 cloak_simple_name(otmp));
896 }
897 if (polyspot)
898 bypass_obj(otmp);
899 m_lose_armor(mon, otmp);
900 }
901 if ((otmp = which_armor(mon, W_ARMU)) != 0) {
902 if (vis) {
903 if (sliparm(mon->data))
904 pline("%s seeps right through %s shirt!", Monnam(mon),
905 ppronoun);
906 else
907 pline("%s becomes much too small for %s shirt!",
908 Monnam(mon), ppronoun);
909 }
910 if (polyspot)
911 bypass_obj(otmp);
912 m_lose_armor(mon, otmp);
913 }
914 }
915 if (handless_or_tiny) {
916 /* [caller needs to handle weapon checks] */
917 if ((otmp = which_armor(mon, W_ARMG)) != 0) {
918 if (vis)
919 pline("%s drops %s gloves%s!", Monnam(mon), ppronoun,
920 MON_WEP(mon) ? " and weapon" : "");
921 if (polyspot)
922 bypass_obj(otmp);
923 m_lose_armor(mon, otmp);
924 }
925 if ((otmp = which_armor(mon, W_ARMS)) != 0) {
926 if (vis)
927 pline("%s can no longer hold %s shield!", Monnam(mon),
928 ppronoun);
929 else
930 You_hear("a clank.");
931 if (polyspot)
932 bypass_obj(otmp);
933 m_lose_armor(mon, otmp);
934 }
935 }
936 if (handless_or_tiny || has_horns(mdat)) {
937 if ((otmp = which_armor(mon, W_ARMH)) != 0
938 /* flimsy test for horns matches polyself handling */
939 && (handless_or_tiny || !is_flimsy(otmp))) {
940 if (vis)
941 pline("%s helmet falls to the %s!", s_suffix(Monnam(mon)),
942 surface(mon->mx, mon->my));
943 else
944 You_hear("a clank.");
945 if (polyspot)
946 bypass_obj(otmp);
947 m_lose_armor(mon, otmp);
948 }
949 }
950 if (handless_or_tiny || slithy(mdat) || mdat->mlet == S_CENTAUR) {
951 if ((otmp = which_armor(mon, W_ARMF)) != 0) {
952 if (vis) {
953 if (is_whirly(mon->data))
954 pline("%s boots fall away!", s_suffix(Monnam(mon)));
955 else
956 pline("%s boots %s off %s feet!", s_suffix(Monnam(mon)),
957 verysmall(mdat) ? "slide" : "are pushed", ppronoun);
958 }
959 if (polyspot)
960 bypass_obj(otmp);
961 m_lose_armor(mon, otmp);
962 }
963 }
964 if (!can_saddle(mon)) {
965 if ((otmp = which_armor(mon, W_SADDLE)) != 0) {
966 if (polyspot)
967 bypass_obj(otmp);
968 m_lose_armor(mon, otmp);
969 if (vis)
970 pline("%s saddle falls off.", s_suffix(Monnam(mon)));
971 }
972 if (mon == u.usteed)
973 goto noride;
974 } else if (mon == u.usteed && !can_ride(mon)) {
975 noride:
976 You("can no longer ride %s.", mon_nam(mon));
977 if (touch_petrifies(u.usteed->data) && !Stone_resistance && rnl(3)) {
978 char buf[BUFSZ];
979
980 You("touch %s.", mon_nam(u.usteed));
981 Sprintf(buf, "falling off %s", an(u.usteed->data->mname));
982 instapetrify(buf);
983 }
984 dismount_steed(DISMOUNT_FELL);
985 }
986 return;
987 }
988
989 /* bias a monster's preferences towards armor that has special benefits. */
990 STATIC_OVL int
extra_pref(mon,obj)991 extra_pref(mon, obj)
992 struct monst *mon;
993 struct obj *obj;
994 {
995 /* currently only does speed boots, but might be expanded if monsters
996 * get to use more armor abilities
997 */
998 if (obj) {
999 if (obj->otyp == SPEED_BOOTS && mon->permspeed != MFAST)
1000 return 20;
1001 }
1002 return 0;
1003 }
1004
1005 /*
1006 * Exceptions to things based on race.
1007 * Correctly checks polymorphed player race.
1008 * Returns:
1009 * 0 No exception, normal rules apply.
1010 * 1 If the race/object combination is acceptable.
1011 * -1 If the race/object combination is unacceptable.
1012 */
1013 int
racial_exception(mon,obj)1014 racial_exception(mon, obj)
1015 struct monst *mon;
1016 struct obj *obj;
1017 {
1018 const struct permonst *ptr = raceptr(mon);
1019
1020 /* Acceptable Exceptions: */
1021 /* Allow hobbits to wear elven armor - LoTR */
1022 if (ptr == &mons[PM_HOBBIT] && is_elven_armor(obj))
1023 return 1;
1024 /* Unacceptable Exceptions: */
1025 /* Checks for object that certain races should never use go here */
1026 /* return -1; */
1027
1028 return 0;
1029 }
1030 /*worn.c*/
1031