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