1 /* SCCS Id: @(#)mthrowu.c 3.4 2003/05/09 */
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 int FDECL(drop_throw,(struct obj *,BOOLEAN_P,int,int));
8
9 #define URETREATING(x,y) (distmin(u.ux,u.uy,x,y) > distmin(u.ux0,u.uy0,x,y))
10
11 #define POLE_LIM 5 /* How far monsters can use pole-weapons */
12
13 #ifndef OVLB
14
15 STATIC_DCL const char *breathwep[];
16
17 #else /* OVLB */
18
19 /*
20 * Keep consistent with breath weapons in zap.c, and AD_* in monattk.h.
21 */
22 STATIC_OVL NEARDATA const char *breathwep[] = {
23 "fragments",
24 "fire",
25 "frost",
26 "sleep gas",
27 "a disintegration blast",
28 "lightning",
29 "poison gas",
30 "acid",
31 "strange breath #8",
32 "strange breath #9"
33 };
34
35 /* hero is hit by something other than a monster */
36 int
thitu(tlev,dam,obj,name)37 thitu(tlev, dam, obj, name)
38 int tlev, dam;
39 struct obj *obj;
40 const char *name; /* if null, then format `obj' */
41 {
42 const char *onm, *knm;
43 boolean is_acid;
44 int kprefix = KILLED_BY_AN;
45 char onmbuf[BUFSZ], knmbuf[BUFSZ];
46
47 if (!name) {
48 if (!obj) panic("thitu: name & obj both null?");
49 name = strcpy(onmbuf,
50 (obj->quan > 1L) ? doname(obj) : mshot_xname(obj));
51 knm = strcpy(knmbuf, killer_xname(obj));
52 kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */
53 } else {
54 knm = name;
55 /* [perhaps ought to check for plural here to] */
56 if (!strncmpi(name, "the ", 4) ||
57 !strncmpi(name, "an ", 3) ||
58 !strncmpi(name, "a ", 2)) kprefix = KILLED_BY;
59 }
60 onm = (obj && obj_is_pname(obj)) ? the(name) :
61 (obj && obj->quan > 1L) ? name : an(name);
62 is_acid = (obj && obj->otyp == ACID_VENOM);
63
64 if(u.uac + tlev <= rnd(20)) {
65 if(Blind || !flags.verbose) pline("It misses.");
66 else You("are almost hit by %s.", onm);
67 return(0);
68 } else {
69 if(Blind || !flags.verbose) You("are hit!");
70 else You("are hit by %s%s", onm, exclam(dam));
71
72 if (obj && objects[obj->otyp].oc_material == SILVER
73 && hates_silver(youmonst.data)) {
74 dam += rnd(20);
75 pline_The("silver sears your flesh!");
76 exercise(A_CON, FALSE);
77 }
78 if (is_acid && Acid_resistance)
79 pline("It doesn't seem to hurt you.");
80 else {
81 if (is_acid) pline("It burns!");
82 if (Half_physical_damage) dam = (dam+1) / 2;
83 losehp(dam, knm, kprefix);
84 exercise(A_STR, FALSE);
85 }
86 return(1);
87 }
88 }
89
90 /* Be sure this corresponds with what happens to player-thrown objects in
91 * dothrow.c (for consistency). --KAA
92 * Returns 0 if object still exists (not destroyed).
93 */
94
95 STATIC_OVL int
drop_throw(obj,ohit,x,y)96 drop_throw(obj, ohit, x, y)
97 register struct obj *obj;
98 boolean ohit;
99 int x,y;
100 {
101 int retvalu = 1;
102 int create;
103 struct monst *mtmp;
104 struct trap *t;
105
106 if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS ||
107 (ohit && obj->otyp == EGG))
108 create = 0;
109 else if (ohit && (is_multigen(obj) || obj->otyp == ROCK))
110 create = !rn2(3);
111 else create = 1;
112
113 if (create && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) &&
114 (t = t_at(x, y)) && ((t->ttyp == PIT) ||
115 (t->ttyp == SPIKED_PIT)))) {
116 int objgone = 0;
117
118 if (down_gate(x, y) != -1)
119 objgone = ship_object(obj, x, y, FALSE);
120 if (!objgone) {
121 if (!flooreffects(obj,x,y,"fall")) { /* don't double-dip on damage */
122 place_object(obj, x, y);
123 if (!mtmp && x == u.ux && y == u.uy)
124 mtmp = &youmonst;
125 if (mtmp && ohit)
126 passive_obj(mtmp, obj, (struct attack *)0);
127 stackobj(obj);
128 retvalu = 0;
129 }
130 }
131 } else obfree(obj, (struct obj*) 0);
132 return retvalu;
133 }
134
135 #endif /* OVLB */
136 #ifdef OVL1
137
138 /* an object launched by someone/thing other than player attacks a monster;
139 return 1 if the object has stopped moving (hit or its range used up) */
140 int
ohitmon(mtmp,otmp,range,verbose)141 ohitmon(mtmp, otmp, range, verbose)
142 struct monst *mtmp; /* accidental target */
143 struct obj *otmp; /* missile; might be destroyed by drop_throw */
144 int range; /* how much farther will object travel if it misses */
145 /* Use -1 to signify to keep going even after hit, */
146 /* unless its gone (used for rolling_boulder_traps) */
147 boolean verbose; /* give message(s) even when you can't see what happened */
148 {
149 int damage, tmp;
150 boolean vis, ismimic;
151 int objgone = 1;
152
153 ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER;
154 vis = cansee(bhitpos.x, bhitpos.y);
155
156 tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE);
157 if (tmp < rnd(20)) {
158 if (!ismimic) {
159 if (vis) miss(distant_name(otmp, mshot_xname), mtmp);
160 else if (verbose) pline("It is missed.");
161 }
162 if (!range) { /* Last position; object drops */
163 (void) drop_throw(otmp, 0, mtmp->mx, mtmp->my);
164 return 1;
165 }
166 } else if (otmp->oclass == POTION_CLASS) {
167 if (ismimic) seemimic(mtmp);
168 mtmp->msleeping = 0;
169 if (vis) otmp->dknown = 1;
170 potionhit(mtmp, otmp, FALSE);
171 return 1;
172 } else {
173 damage = dmgval(otmp, mtmp);
174 if (otmp->otyp == ACID_VENOM && resists_acid(mtmp))
175 damage = 0;
176 if (ismimic) seemimic(mtmp);
177 mtmp->msleeping = 0;
178 if (vis) hit(distant_name(otmp,mshot_xname), mtmp, exclam(damage));
179 else if (verbose) pline("%s is hit%s", Monnam(mtmp), exclam(damage));
180
181 #ifdef WEBB_DISINT
182 if (touch_disintegrates(mtmp->data) && !mtmp->mcan && mtmp->mhp>6 &&
183 !oresist_disintegration(otmp)){
184 damage = otmp->owt;
185 weight_dmg(damage);
186 mtmp->mhp-=damage;
187 if(vis)
188 pline("It disintegrates!");
189 obfree(otmp, (struct obj*) 0);
190 return 1;
191 }
192 #endif
193 if (otmp->opoisoned && is_poisonable(otmp)) {
194 if (resists_poison(mtmp)) {
195 if (vis) pline_The("poison doesn't seem to affect %s.",
196 mon_nam(mtmp));
197 } else {
198 if (rn2(30)) {
199 damage += rnd(6);
200 } else {
201 if (vis) pline_The("poison was deadly...");
202 damage = mtmp->mhp;
203 }
204 }
205 }
206 if (objects[otmp->otyp].oc_material == SILVER &&
207 hates_silver(mtmp->data)) {
208 if (vis) pline_The("silver sears %s flesh!",
209 s_suffix(mon_nam(mtmp)));
210 else if (verbose) pline("Its flesh is seared!");
211 }
212 if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx,mtmp->my)) {
213 if (resists_acid(mtmp)) {
214 if (vis || verbose)
215 pline("%s is unaffected.", Monnam(mtmp));
216 damage = 0;
217 } else {
218 if (vis) pline_The("acid burns %s!", mon_nam(mtmp));
219 else if (verbose) pline("It is burned!");
220 }
221 }
222 mtmp->mhp -= damage;
223 if (mtmp->mhp < 1) {
224 if (vis || verbose)
225 pline("%s is %s!", Monnam(mtmp),
226 (nonliving(mtmp->data) || !canspotmon(mtmp))
227 ? "destroyed" : "killed");
228 /* don't blame hero for unknown rolling boulder trap */
229 if (!flags.mon_moving &&
230 (otmp->otyp != BOULDER || range >= 0 || !otmp->otrapped))
231 xkilled(mtmp,0);
232 else mondied(mtmp);
233 }
234
235 if (can_blnd((struct monst*)0, mtmp,
236 (uchar)(otmp->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP),
237 otmp)) {
238 if (vis && mtmp->mcansee)
239 pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp)));
240 mtmp->mcansee = 0;
241 tmp = (int)mtmp->mblinded + rnd(25) + 20;
242 if (tmp > 127) tmp = 127;
243 mtmp->mblinded = tmp;
244 }
245
246 objgone = drop_throw(otmp, 1, bhitpos.x, bhitpos.y);
247 if (!objgone && range == -1) { /* special case */
248 obj_extract_self(otmp); /* free it for motion again */
249 return 0;
250 }
251 return 1;
252 }
253 return 0;
254 }
255
256 void
m_throw(mon,x,y,dx,dy,range,obj)257 m_throw(mon, x, y, dx, dy, range, obj)
258 register struct monst *mon;
259 register int x,y,dx,dy,range; /* direction and range */
260 register struct obj *obj;
261 {
262 register struct monst *mtmp;
263 struct obj *singleobj;
264 char sym = obj->oclass;
265 int hitu, blindinc = 0;
266
267 bhitpos.x = x;
268 bhitpos.y = y;
269
270 if (obj->quan == 1L) {
271 /*
272 * Remove object from minvent. This cannot be done later on;
273 * what if the player dies before then, leaving the monster
274 * with 0 daggers? (This caused the infamous 2^32-1 orcish
275 * dagger bug).
276 *
277 * VENOM is not in minvent - it should already be OBJ_FREE.
278 * The extract below does nothing.
279 */
280
281 /* not possibly_unwield, which checks the object's */
282 /* location, not its existence */
283 if (MON_WEP(mon) == obj) {
284 setmnotwielded(mon,obj);
285 MON_NOWEP(mon);
286 }
287 obj_extract_self(obj);
288 singleobj = obj;
289 obj = (struct obj *) 0;
290 } else {
291 singleobj = splitobj(obj, 1L);
292 obj_extract_self(singleobj);
293 }
294
295 singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */
296
297 if (singleobj->cursed && (dx || dy) && !rn2(7)) {
298 if(canseemon(mon) && flags.verbose) {
299 if(is_ammo(singleobj))
300 pline("%s misfires!", Monnam(mon));
301 else
302 pline("%s as %s throws it!",
303 Tobjnam(singleobj, "slip"), mon_nam(mon));
304 }
305 dx = rn2(3)-1;
306 dy = rn2(3)-1;
307 /* check validity of new direction */
308 if (!dx && !dy) {
309 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
310 return;
311 }
312 }
313
314 /* pre-check for doors, walls and boundaries.
315 Also need to pre-check for bars regardless of direction;
316 the random chance for small objects hitting bars is
317 skipped when reaching them at point blank range */
318 if (!isok(bhitpos.x+dx,bhitpos.y+dy)
319 || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ)
320 || closed_door(bhitpos.x+dx, bhitpos.y+dy)
321 || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS &&
322 hits_bars(&singleobj, bhitpos.x, bhitpos.y, 0, 0))) {
323 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
324 return;
325 }
326
327 /* Note: drop_throw may destroy singleobj. Since obj must be destroyed
328 * early to avoid the dagger bug, anyone who modifies this code should
329 * be careful not to use either one after it's been freed.
330 */
331 if (sym) tmp_at(DISP_FLASH, obj_to_glyph(singleobj));
332 while(range-- > 0) { /* Actually the loop is always exited by break */
333 bhitpos.x += dx;
334 bhitpos.y += dy;
335 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
336 if (ohitmon(mtmp, singleobj, range, TRUE))
337 break;
338 } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
339 if (multi) nomul(0, 0);
340
341 if (singleobj->oclass == GEM_CLASS &&
342 singleobj->otyp <= LAST_GEM+9 /* 9 glass colors */
343 && is_unicorn(youmonst.data)) {
344 if (singleobj->otyp > LAST_GEM) {
345 You("catch the %s.", xname(singleobj));
346 You("are not interested in %s junk.",
347 s_suffix(mon_nam(mon)));
348 makeknown(singleobj->otyp);
349 dropy(singleobj);
350 } else {
351 You("accept %s gift in the spirit in which it was intended.",
352 s_suffix(mon_nam(mon)));
353 (void)hold_another_object(singleobj,
354 "You catch, but drop, %s.", xname(singleobj),
355 "You catch:");
356 }
357 break;
358 }
359 if (singleobj->oclass == POTION_CLASS) {
360 if (!Blind) singleobj->dknown = 1;
361 potionhit(&youmonst, singleobj, FALSE);
362 break;
363 }
364 switch(singleobj->otyp) {
365 int dam, hitv;
366 case EGG:
367 if (!touch_petrifies(&mons[singleobj->corpsenm])) {
368 warning("monster throwing egg type %d",
369 singleobj->corpsenm);
370 hitu = 0;
371 break;
372 }
373 /* fall through */
374 case CREAM_PIE:
375 case BLINDING_VENOM:
376 hitu = thitu(8, 0, singleobj, (char *)0);
377 break;
378 default:
379 dam = dmgval(singleobj, &youmonst);
380 hitv = 3 - distmin(u.ux,u.uy, mon->mx,mon->my);
381 if (hitv < -4) hitv = -4;
382 if (is_elf(mon->data) &&
383 objects[singleobj->otyp].oc_skill == P_BOW) {
384 hitv++;
385 if (MON_WEP(mon) &&
386 MON_WEP(mon)->otyp == ELVEN_BOW)
387 hitv++;
388 if(singleobj->otyp == ELVEN_ARROW) dam++;
389 }
390 if (bigmonst(youmonst.data)) hitv++;
391 hitv += 8 + singleobj->spe;
392 if (dam < 1) dam = 1;
393 hitu = thitu(hitv, dam, singleobj, (char *)0);
394 }
395 if (hitu && singleobj->opoisoned &&
396 is_poisonable(singleobj)) {
397 char onmbuf[BUFSZ], knmbuf[BUFSZ];
398
399 Strcpy(onmbuf, xname(singleobj));
400 Strcpy(knmbuf, killer_xname(singleobj));
401 poisoned(onmbuf, A_STR, knmbuf, -10);
402 }
403 if(hitu &&
404 can_blnd((struct monst*)0, &youmonst,
405 (uchar)(singleobj->otyp == BLINDING_VENOM ?
406 AT_SPIT : AT_WEAP), singleobj)) {
407 blindinc = rnd(25);
408 if(singleobj->otyp == CREAM_PIE) {
409 if(!Blind) pline("Yecch! You've been %s.",
410 piday() ? "pied" : "creamed");
411 else pline("There's %s sticky all over your %s.",
412 something,
413 body_part(FACE));
414 } else if(singleobj->otyp == BLINDING_VENOM) {
415 int num_eyes = eyecount(youmonst.data);
416 /* venom in the eyes */
417 if(!Blind) pline_The("venom blinds you.");
418 else Your("%s sting%s.",
419 (num_eyes == 1) ? body_part(EYE) :
420 makeplural(body_part(EYE)),
421 (num_eyes == 1) ? "s" : "");
422 }
423 }
424 if (hitu && singleobj->otyp == EGG) {
425 if (!Stone_resistance
426 && !(poly_when_stoned(youmonst.data) &&
427 polymon(PM_STONE_GOLEM))) {
428 Stoned = 5;
429 killer = (char *) 0;
430 }
431 }
432 stop_occupation();
433 if (hitu || !range) {
434 (void) drop_throw(singleobj, hitu, u.ux, u.uy);
435 break;
436 }
437 } else if (!range /* reached end of path */
438 /* missile hits edge of screen */
439 || !isok(bhitpos.x+dx,bhitpos.y+dy)
440 /* missile hits the wall */
441 || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ)
442 /* missile hit closed door */
443 || closed_door(bhitpos.x+dx, bhitpos.y+dy)
444 /* missile might hit iron bars */
445 || (levl[bhitpos.x+dx][bhitpos.y+dy].typ == IRONBARS &&
446 hits_bars(&singleobj, bhitpos.x, bhitpos.y, !rn2(5), 0))
447 #ifdef SINKS
448 /* Thrown objects "sink" */
449 || IS_SINK(levl[bhitpos.x][bhitpos.y].typ)
450 #endif
451 ) {
452 if (singleobj) /* hits_bars might have destroyed it */
453 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
454 break;
455 }
456 tmp_at(bhitpos.x, bhitpos.y);
457 delay_output();
458 }
459 tmp_at(bhitpos.x, bhitpos.y);
460 delay_output();
461 tmp_at(DISP_END, 0);
462
463 if (blindinc) {
464 u.ucreamed += blindinc;
465 make_blinded(Blinded + (long)blindinc, FALSE);
466 if (!Blind) Your(vision_clears);
467 }
468 }
469
470 #endif /* OVL1 */
471 #ifdef OVLB
472
473 /* Remove an item from the monster's inventory and destroy it. */
474 void
m_useup(mon,obj)475 m_useup(mon, obj)
476 struct monst *mon;
477 struct obj *obj;
478 {
479 if (obj->quan > 1L) {
480 obj->quan--;
481 obj->owt = weight(obj);
482 } else {
483 obj_extract_self(obj);
484 possibly_unwield(mon, FALSE);
485 if (obj->owornmask) {
486 mon->misc_worn_check &= ~obj->owornmask;
487 update_mon_intrinsics(mon, obj, FALSE, FALSE);
488 }
489 obfree(obj, (struct obj*) 0);
490 }
491 }
492
493 #endif /* OVLB */
494 #ifdef OVL1
495
496 /* monster attempts ranged weapon attack against player */
497 void
thrwmu(mtmp)498 thrwmu(mtmp)
499 struct monst *mtmp;
500 {
501 struct obj *otmp, *mwep;
502 xchar x, y;
503 schar skill;
504 int multishot;
505 const char *onm;
506
507 /* Rearranged beginning so monsters can use polearms not in a line */
508 if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
509 mtmp->weapon_check = NEED_RANGED_WEAPON;
510 /* mon_wield_item resets weapon_check as appropriate */
511 if(mon_wield_item(mtmp) != 0) return;
512 }
513
514 /* Pick a weapon */
515 otmp = select_rwep(mtmp);
516 if (!otmp) return;
517
518 if (is_pole(otmp)) {
519 int dam, hitv;
520
521 if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) > POLE_LIM ||
522 !couldsee(mtmp->mx, mtmp->my))
523 return; /* Out of range, or intervening wall */
524
525 if (canseemon(mtmp)) {
526 onm = xname(otmp);
527 pline("%s thrusts %s.", Monnam(mtmp),
528 obj_is_pname(otmp) ? the(onm) : an(onm));
529 }
530
531 dam = dmgval(otmp, &youmonst);
532 hitv = 3 - distmin(u.ux,u.uy, mtmp->mx,mtmp->my);
533 if (hitv < -4) hitv = -4;
534 if (bigmonst(youmonst.data)) hitv++;
535 hitv += 8 + otmp->spe;
536 if (dam < 1) dam = 1;
537
538 (void) thitu(hitv, dam, otmp, (char *)0);
539 stop_occupation();
540 return;
541 }
542
543 x = mtmp->mx;
544 y = mtmp->my;
545 /* If you are coming toward the monster, the monster
546 * should try to soften you up with missiles. If you are
547 * going away, you are probably hurt or running. Give
548 * chase, but if you are getting too far away, throw.
549 */
550 if (!lined_up(mtmp) ||
551 (URETREATING(x,y) &&
552 rn2(BOLT_LIM - distmin(x,y,mtmp->mux,mtmp->muy))))
553 return;
554
555 skill = objects[otmp->otyp].oc_skill;
556 mwep = MON_WEP(mtmp); /* wielded weapon */
557
558 /* Multishot calculations */
559 multishot = 1;
560 if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER ||
561 skill == -P_DART || skill == -P_SHURIKEN) && !mtmp->mconf) {
562 /* Assumes lords are skilled, princes are expert */
563 if (is_prince(mtmp->data)) multishot += 2;
564 else if (is_lord(mtmp->data)) multishot++;
565
566 switch (monsndx(mtmp->data)) {
567 case PM_RANGER:
568 multishot++;
569 break;
570 case PM_ROGUE:
571 if (skill == P_DAGGER) multishot++;
572 break;
573 case PM_NINJA:
574 case PM_SAMURAI:
575 if (otmp->otyp == YA && mwep &&
576 mwep->otyp == YUMI) multishot++;
577 break;
578 default:
579 break;
580 }
581 /* racial bonus */
582 if ((is_elf(mtmp->data) &&
583 otmp->otyp == ELVEN_ARROW &&
584 mwep && mwep->otyp == ELVEN_BOW) ||
585 (is_orc(mtmp->data) &&
586 otmp->otyp == ORCISH_ARROW &&
587 mwep && mwep->otyp == ORCISH_BOW))
588 multishot++;
589
590 if ((long)multishot > otmp->quan) multishot = (int)otmp->quan;
591 if (multishot < 1) multishot = 1;
592 else multishot = rnd(multishot);
593 }
594
595 if (canseemon(mtmp)) {
596 char onmbuf[BUFSZ];
597
598 if (multishot > 1) {
599 /* "N arrows"; multishot > 1 implies otmp->quan > 1, so
600 xname()'s result will already be pluralized */
601 Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
602 onm = onmbuf;
603 } else {
604 /* "an arrow" */
605 onm = singular(otmp, xname);
606 onm = obj_is_pname(otmp) ? the(onm) : an(onm);
607 }
608 m_shot.s = ammo_and_launcher(otmp,mwep) ? TRUE : FALSE;
609 pline("%s %s %s!", Monnam(mtmp),
610 m_shot.s ? "shoots" : "throws", onm);
611 m_shot.o = otmp->otyp;
612 } else {
613 m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */
614 }
615
616 m_shot.n = multishot;
617 for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++)
618 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
619 distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
620 m_shot.n = m_shot.i = 0;
621 m_shot.o = STRANGE_OBJECT;
622 m_shot.s = FALSE;
623
624 nomul(0, 0);
625 }
626
627 #endif /* OVL1 */
628 #ifdef OVLB
629
630 int
spitmu(mtmp,mattk)631 spitmu(mtmp, mattk) /* monster spits substance at you */
632 register struct monst *mtmp;
633 register struct attack *mattk;
634 {
635 register struct obj *otmp;
636
637 if(mtmp->mcan) {
638
639 if(flags.soundok)
640 pline("A dry rattle comes from %s throat.",
641 s_suffix(mon_nam(mtmp)));
642 return 0;
643 }
644 if(lined_up(mtmp)) {
645 switch (mattk->adtyp) {
646 case AD_BLND:
647 case AD_DRST:
648 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
649 break;
650 default:
651 warning("bad attack type in spitmu");
652 /* fall through */
653 case AD_ACID:
654 otmp = mksobj(ACID_VENOM, TRUE, FALSE);
655 break;
656 }
657 if(!rn2(BOLT_LIM-distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy))) {
658 if (canseemon(mtmp))
659 pline("%s spits venom!", Monnam(mtmp));
660 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
661 distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy), otmp);
662 nomul(0, 0);
663 return 0;
664 }
665 }
666 return 0;
667 }
668
669 #endif /* OVLB */
670 #ifdef OVL1
671
672 int
breamu(mtmp,mattk)673 breamu(mtmp, mattk) /* monster breathes at you (ranged) */
674 register struct monst *mtmp;
675 register struct attack *mattk;
676 {
677 /* if new breath types are added, change AD_ACID to max type */
678 int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp ;
679
680 if(lined_up(mtmp)) {
681
682 if(mtmp->mcan) {
683 if(flags.soundok) {
684 if(canseemon(mtmp))
685 pline("%s coughs.", Monnam(mtmp));
686 else
687 You_hear("a cough.");
688 }
689 return(0);
690 }
691 if(!mtmp->mspec_used && rn2(3)) {
692
693 if((typ >= AD_MAGM) && (typ <= AD_ACID)) {
694
695 if(canseemon(mtmp))
696 pline("%s breathes %s!", Monnam(mtmp),
697 breathwep[typ-1]);
698 buzz((int) (-20 - (typ-1)), (int)mattk->damn,
699 mtmp->mx, mtmp->my, sgn(tbx), sgn(tby));
700 nomul(0, 0);
701 /* breath runs out sometimes. Also, give monster some
702 * cunning; don't breath if the player fell asleep.
703 */
704 if(!rn2(3))
705 mtmp->mspec_used = 10+rn2(20);
706 if(typ == AD_SLEE && !Sleep_resistance)
707 mtmp->mspec_used += rnd(20);
708 } else warning("Breath weapon %d used", typ-1);
709 }
710 }
711 return(1);
712 }
713
714 boolean
linedup(ax,ay,bx,by)715 linedup(ax, ay, bx, by)
716 register xchar ax, ay, bx, by;
717 {
718 tbx = ax - bx; /* These two values are set for use */
719 tby = ay - by; /* after successful return. */
720
721 /* sometimes displacement makes a monster think that you're at its
722 own location; prevent it from throwing and zapping in that case */
723 if (!tbx && !tby) return FALSE;
724
725 if((!tbx || !tby || abs(tbx) == abs(tby)) /* straight line or diagonal */
726 && distmin(tbx, tby, 0, 0) < BOLT_LIM) {
727 if(ax == u.ux && ay == u.uy) return((boolean)(couldsee(bx,by)));
728 else if(clear_path(ax,ay,bx,by)) return TRUE;
729 }
730 return FALSE;
731 }
732
733 boolean
lined_up(mtmp)734 lined_up(mtmp) /* is mtmp in position to use ranged attack? */
735 register struct monst *mtmp;
736 {
737 return(linedup(mtmp->mux,mtmp->muy,mtmp->mx,mtmp->my));
738 }
739
740 #endif /* OVL1 */
741 #ifdef OVL0
742
743 /* Check if a monster is carrying a particular item.
744 */
745 struct obj *
m_carrying(mtmp,type)746 m_carrying(mtmp, type)
747 struct monst *mtmp;
748 int type;
749 {
750 register struct obj *otmp;
751
752 for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
753 if(otmp->otyp == type)
754 return(otmp);
755 return((struct obj *) 0);
756 }
757
758 /* TRUE iff thrown/kicked/rolled object doesn't pass through iron bars */
759 boolean
hits_bars(obj_p,x,y,always_hit,whodidit)760 hits_bars(obj_p, x, y, always_hit, whodidit)
761 struct obj **obj_p; /* *obj_p will be set to NULL if object breaks */
762 int x, y;
763 int always_hit; /* caller can force a hit for items which would fit through */
764 int whodidit; /* 1==hero, 0=other, -1==just check whether it'll pass thru */
765 {
766 struct obj *otmp = *obj_p;
767 int obj_type = otmp->otyp;
768 boolean hits = always_hit;
769
770 if (!hits)
771 switch (otmp->oclass) {
772 case WEAPON_CLASS:
773 {
774 int oskill = objects[obj_type].oc_skill;
775
776 hits = (oskill != -P_BOW && oskill != -P_CROSSBOW &&
777 oskill != -P_DART && oskill != -P_SHURIKEN &&
778 oskill != P_SPEAR && oskill != P_JAVELIN &&
779 oskill != P_KNIFE); /* but not dagger */
780 break;
781 }
782 case ARMOR_CLASS:
783 hits = (objects[obj_type].oc_armcat != ARM_GLOVES);
784 break;
785 case TOOL_CLASS:
786 hits = (obj_type != SKELETON_KEY &&
787 obj_type != LOCK_PICK &&
788 #ifdef TOURIST
789 obj_type != CREDIT_CARD &&
790 #endif
791 obj_type != TALLOW_CANDLE &&
792 obj_type != WAX_CANDLE &&
793 obj_type != LENSES &&
794 obj_type != TIN_WHISTLE &&
795 obj_type != MAGIC_WHISTLE);
796 break;
797 case ROCK_CLASS: /* includes boulder */
798 if (obj_type != STATUE ||
799 mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE;
800 break;
801 case FOOD_CLASS:
802 if (obj_type == CORPSE &&
803 mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE;
804 else
805 hits = (obj_type == MEAT_STICK ||
806 obj_type == HUGE_CHUNK_OF_MEAT);
807 break;
808 case SPBOOK_CLASS:
809 case WAND_CLASS:
810 case BALL_CLASS:
811 case CHAIN_CLASS:
812 hits = TRUE;
813 break;
814 default:
815 break;
816 }
817
818 if (hits && whodidit != -1) {
819 if (whodidit ? hero_breaks(otmp, x, y, FALSE) : breaks(otmp, x, y))
820 *obj_p = otmp = 0; /* object is now gone */
821 /* breakage makes its own noises */
822 else if (obj_type == BOULDER || obj_type == HEAVY_IRON_BALL)
823 pline("Whang!");
824 else if (otmp->oclass == COIN_CLASS ||
825 objects[obj_type].oc_material == GOLD ||
826 objects[obj_type].oc_material == SILVER)
827 pline("Clink!");
828 else
829 pline("Clonk!");
830 }
831
832 return hits;
833 }
834
835 #endif /* OVL0 */
836
837 /*mthrowu.c*/
838