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