1    | /*	SCCS Id: @(#)mthrowu.c	3.3	2000/07/07	*/
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   | 
36   | int
37   | thitu(tlev, dam, obj, name)	/* u is hit by sth, but not a monster */
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   | 	char onmbuf[BUFSZ], knmbuf[BUFSZ];
45   | 
46   | 	if (!name) {
47   | 	    struct obj otmp;
48   | 	    unsigned save_ocknown;
49   | 
50   | 	    if (!obj) panic("thitu: name & obj both null?");
51   | 	    name = strcpy(onmbuf, (obj->quan > 1L) ? doname(obj) : xname(obj));
52   | 	    /* killer name should be more specific; however, exact info
53   | 	       like blessed/cursed and rustproof make things too verbose */
54   | 	    otmp = *obj;
55   | 	    save_ocknown = objects[otmp.otyp].oc_name_known;
56   | 	    otmp.known = otmp.dknown = 1;
57   | 	    otmp.bknown = otmp.rknown = otmp.greased = 0;
58   | 	    /* "killed by poisoned <obj>" would be misleading
59   | 	       since poison is not the cause of death */
60   | 	    otmp.opoisoned = 0;
61   | 	    objects[otmp.otyp].oc_name_known = 1;
62   | 	    knm = strcpy(knmbuf,
63   | 			 (otmp.quan > 1L) ? doname(&otmp) : xname(&otmp));
64   | 	    objects[otmp.otyp].oc_name_known = save_ocknown;
65   | 	} else {
66   | 	    knm = name;
67   | 	}
68   | 	onm = (obj && obj_is_pname(obj)) ? the(name) :
69   | 			    (obj && obj->quan > 1L) ? name : an(name);
70   | 	is_acid = (obj && obj->otyp == ACID_VENOM);
71   | 
72   | 	if(u.uac + tlev <= rnd(20)) {
73   | 		if(Blind || !flags.verbose) pline("It misses.");
74   | 		else You("are almost hit by %s!", onm);
75   | 		return(0);
76   | 	} else {
77   | 		if(Blind || !flags.verbose) You("are hit!");
78   | 		else You("are hit by %s!", onm);
79   | 
80   | 		if (obj && objects[obj->otyp].oc_material == SILVER
81   | 				&& hates_silver(youmonst.data)) {
82   | 			dam += rnd(20);
83   | 			pline_The("silver sears your flesh!");
84   | 			exercise(A_CON, FALSE);
85   | 		}
86   | 		if (is_acid && Acid_resistance)
87   | 			pline("It doesn't seem to hurt you.");
88   | 		else {
89   | 			if (is_acid) pline("It burns!");
90   | 			if (Half_physical_damage) dam = (dam+1) / 2;
91   | 			losehp(dam, knm, (obj && obj_is_pname(obj)) ?
92   | 			       KILLED_BY : KILLED_BY_AN);
93   | 			exercise(A_STR, FALSE);
94   | 		}
95   | 		return(1);
96   | 	}
97   | }
98   | 
99   | /* Be sure this corresponds with what happens to player-thrown objects in
100  |  * dothrow.c (for consistency). --KAA
101  |  * Returns 0 if object still exists (not destroyed).
102  |  */
103  | 
104  | STATIC_OVL int
105  | drop_throw(obj, ohit, x, y)
106  | register struct obj *obj;
107  | boolean ohit;
108  | int x,y;
109  | {
110  | 	int retvalu = 1;
111  | 	int create;
112  | 	struct monst *mtmp;
113  | 	struct trap *t;
114  | 
115  | 	if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS ||
116  | 		    (ohit && obj->otyp == EGG))
117  | 		create = 0;
118  | 	else if (ohit && (is_multigen(obj) || obj->otyp == ROCK))
119  | 		create = !rn2(3);
120  | 	else create = 1;
121  | 
122  | 	if (create && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) &&
123  | 			(t = t_at(x, y)) && ((t->ttyp == PIT) ||
124  | 			(t->ttyp == SPIKED_PIT)))) {
125  | 		int objgone = 0;
126  | 
127  | 		if (down_gate(x, y) != -1)
128  | 			objgone = ship_object(obj, x, y, FALSE);
129  | 		if (!objgone) {
130  | 			if (!flooreffects(obj,x,y,"fall")) { /* don't double-dip on damage */
131  | 			    place_object(obj, x, y);
132  | 			    stackobj(obj);
133  | 			    retvalu = 0;
134  | 			}
135  | 		}
136  | 	} else obfree(obj, (struct obj*) 0);
137  | 	return retvalu;
138  | }
139  | 
140  | #endif /* OVLB */
141  | #ifdef OVL1
142  | 
143  | /* an object launched by someone/thing other than player attacks a monster;
144  |    return 1 if the object has stopped moving (hit or its range used up) */
145  | int
146  | ohitmon(mtmp, otmp, range, verbose)
147  | struct monst *mtmp;	/* accidental target */
148  | struct obj *otmp;	/* missile; might be destroyed by drop_throw */
149  | int range;		/* how much farther will object travel if it misses */
150  | 			/* Use -1 to signify to keep going even after hit, */
151  | 			/* unless its gone (used for rolling_boulder_traps) */
152  | boolean verbose;  /* give message(s) even when you can't see what happened */
153  | {
154  | 	int damage, tmp;
155  | 	boolean vis, ismimic;
156  | 	int objgone = 1;
157  | 
158  | 	ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER;
159  | 	vis = cansee(bhitpos.x, bhitpos.y);
160  | 
161  | 	tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE);
162  | 	if (tmp < rnd(20)) {
163  | 	    if (!ismimic) {
164  | 		if (vis) miss(distant_name(otmp, xname), mtmp);
165  | 		else if (verbose) pline("It is missed.");
166  | 	    }
167  | 	    if (!range) { /* Last position; object drops */
168  | 		(void) drop_throw(otmp, 0, mtmp->mx, mtmp->my);
169  | 		return 1;
170  | 	    }
171  | 	} else if (otmp->oclass == POTION_CLASS) {
172  | 	    if (ismimic) seemimic(mtmp);
173  | 	    mtmp->msleeping = 0;
174  | 	    if (vis) otmp->dknown = 1;
175  | 	    potionhit(mtmp, otmp, FALSE);
176  | 	    return 1;
177  | 	} else {
178  | 	    damage = dmgval(otmp, mtmp);
179  | 	    if (otmp->otyp == ACID_VENOM && resists_acid(mtmp))
180  | 		damage = 0;
181  | 	    if (ismimic) seemimic(mtmp);
182  | 	    mtmp->msleeping = 0;
183  | 	    if (vis) hit(distant_name(otmp,xname), mtmp, exclam(damage));
184  | 	    else if (verbose) pline("It is hit%s", exclam(damage));
185  | 
186  | 	    if (otmp->opoisoned) {
187  | 		if (resists_poison(mtmp)) {
188  | 		    if (vis) pline_The("poison doesn't seem to affect %s.",
189  | 				   mon_nam(mtmp));
190  | 		} else {
191  | 		    if (rn2(30)) {
192  | 			damage += rnd(6);
193  | 		    } else {
194  | 			if (vis) pline_The("poison was deadly...");
195  | 			damage = mtmp->mhp;
196  | 		    }
197  | 		}
198  | 	    }
199  | 	    if (objects[otmp->otyp].oc_material == SILVER &&
200  | 		    hates_silver(mtmp->data)) {
201  | 		if (vis) pline_The("silver sears %s flesh!",
202  | 				s_suffix(mon_nam(mtmp)));
203  | 		else if (verbose) pline("Its flesh is seared!");
204  | 	    }
205  | 	    if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx,mtmp->my)) {
206  | 		if (resists_acid(mtmp)) {
207  | 		    if (vis || verbose)
208  | 			pline("%s is unaffected.", Monnam(mtmp));
209  | 		    damage = 0;
210  | 		} else {
211  | 		    if (vis) pline_The("acid burns %s!", mon_nam(mtmp));
212  | 		    else if (verbose) pline("It is burned!");
213  | 		}
214  | 	    }
215  | 	    mtmp->mhp -= damage;
216  | 	    if (mtmp->mhp < 1) {
217  | 		if (vis || verbose)
218  | 		    pline("%s is %s!", Monnam(mtmp),
219  | 			(nonliving(mtmp->data) || !vis)
220  | 			? "destroyed" : "killed");
221  | 		mondied(mtmp);
222  | 	    }
223  | 
224  | 	    if (can_blnd((struct monst*)0, mtmp,
225  | 		    (uchar)(otmp->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP),
226  | 		    otmp)) {
227  | 		if (vis && mtmp->mcansee)
228  | 		    pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp)));
229  | 		mtmp->mcansee = 0;
230  | 		tmp = (int)mtmp->mblinded + rnd(25) + 20;
231  | 		if (tmp > 127) tmp = 127;
232  | 		mtmp->mblinded = tmp;
233  | 	    }
234  | 
235  | 	    objgone = drop_throw(otmp, 1, bhitpos.x, bhitpos.y);
236  | 	    if (!objgone && range == -1) {  /* special case */
237  | 		    obj_extract_self(otmp); /* free it for motion again */
238  | 		    return 0;
239  | 	    }
240  | 	    return 1;
241  | 	}
242  | 	return 0;
243  | }
244  | 
245  | void
246  | m_throw(mon, x, y, dx, dy, range, obj)
247  | 	register struct monst *mon;
248  | 	register int x,y,dx,dy,range;		/* direction and range */
249  | 	register struct obj *obj;
250  | {
251  | 	register struct monst *mtmp;
252  | 	struct obj *singleobj;
253  | 	char sym = obj->oclass;
254  | 	int hitu, blindinc = 0;
255  | 
256  | 	bhitpos.x = x;
257  | 	bhitpos.y = y;
258  | 
259  | 	if (obj->quan == 1L) {
260  | 	    /*
261  | 	     * Remove object from minvent.  This cannot be done later on;
262  | 	     * what if the player dies before then, leaving the monster
263  | 	     * with 0 daggers?  (This caused the infamous 2^32-1 orcish
264  | 	     * dagger bug).
265  | 	     *
266  | 	     * VENOM is not in minvent - it should already be OBJ_FREE.
267  | 	     * The extract below does nothing.
268  | 	     */
269  | 
270  | 	    /* not possibly_unwield, which checks the object's */
271  | 	    /* location, not its existence */
272  | 	    if (MON_WEP(mon) == obj) {
273  | 		    obj->owornmask &= ~W_WEP;
274  | 		    MON_NOWEP(mon);
275  | 	    }
276  | 	    obj_extract_self(obj);
277  | 	    singleobj = obj;
278  | 	    obj = (struct obj *) 0;
279  | 	} else {
280  | 	    singleobj = splitobj(obj, obj->quan - 1L);
281  | 	    obj_extract_self(singleobj);
282  | 	}
283  | 
284  | 	singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */
285  | 
286  | 	if (singleobj->cursed && (dx || dy) && !rn2(7)) {
287  | 	    if(canseemon(mon) && flags.verbose) {
288  | 		if(is_ammo(singleobj))
289  | 		    pline("%s misfires!", Monnam(mon));
290  | 		else
291  | 		    pline("%s slips as %s throws it!",
292  | 			  The(xname(singleobj)), mon_nam(mon));
293  | 	    }
294  | 	    dx = rn2(3)-1;
295  | 	    dy = rn2(3)-1;
296  | 	    /* pre-check validity of new direction */
297  | 	    if((!dx && !dy)
298  | 	       || !isok(bhitpos.x+dx,bhitpos.y+dy)
299  | 	       /* missile hits the wall */
300  | 	       || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ)) {
301  | 		(void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
302  | 		return;
303  | 	    }
304  | 	}
305  | 
306  | 	/* Note: drop_throw may destroy singleobj.  Since obj must be destroyed
307  | 	 * early to avoid the dagger bug, anyone who modifies this code should
308  | 	 * be careful not to use either one after it's been freed.
309  | 	 */
310  | 	if (sym) tmp_at(DISP_FLASH, obj_to_glyph(singleobj));
311  | 	while(range-- > 0) { /* Actually the loop is always exited by break */
312  | 		bhitpos.x += dx;
313  | 		bhitpos.y += dy;
314  | 		if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
315  | 		    if (ohitmon(mtmp, singleobj, range, TRUE))
316  | 			break;
317  | 		} else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
318  | 		    if (multi) nomul(0);
319  | 
320  | 		    if (singleobj->oclass == GEM_CLASS &&
321  | 			    singleobj->otyp <= LAST_GEM+9 /* 9 glass colors */
322  | 			    && is_unicorn(youmonst.data)) {
323  | 			if (singleobj->otyp > LAST_GEM) {
324  | 			    You("catch the %s.", xname(singleobj));
325  | 			    You("are not interested in %s junk.",
326  | 				s_suffix(mon_nam(mon)));
327  | 			    makeknown(singleobj->otyp);
328  | 			    dropy(singleobj);
329  | 			} else {
330  | 			    You("accept %s gift in the spirit in which it was intended.",
331  | 				s_suffix(mon_nam(mon)));
332  | 			    (void)hold_another_object(singleobj,
333  | 				"You catch, but drop, %s.", xname(singleobj),
334  | 				"You catch:");
335  | 			}
336  | 			break;
337  | 		    }
338  | 		    if (singleobj->oclass == POTION_CLASS) {
339  | 			if (!Blind) singleobj->dknown = 1;
340  | 			potionhit(&youmonst, singleobj, FALSE);
341  | 			break;
342  | 		    }
343  | 		    switch(singleobj->otyp) {
344  | 			int dam, hitv;
345  | 			case EGG:
346  | 			    if (!touch_petrifies(&mons[singleobj->corpsenm])) {
347  | 				impossible("monster throwing egg type %d",
348  | 					singleobj->corpsenm);
349  | 				hitu = 0;
350  | 				break;
351  | 			    }
352  | 			    /* fall through */
353  | 			case CREAM_PIE:
354  | 			case BLINDING_VENOM:
355  | 			    hitu = thitu(8, 0, singleobj, (char *)0);
356  | 			    break;
357  | 			default:
358  | 			    dam = dmgval(singleobj, &youmonst);
359  | 			    hitv = 3 - distmin(u.ux,u.uy, mon->mx,mon->my);
360  | 			    if (hitv < -4) hitv = -4;
361  | 			    if (is_elf(mon->data) &&
362  | 				objects[singleobj->otyp].oc_skill == P_BOW) {
363  | 				hitv++;
364  | 				if (MON_WEP(mon) &&
365  | 				    MON_WEP(mon)->otyp == ELVEN_BOW)
366  | 				    hitv++;
367  | 				if(singleobj->otyp == ELVEN_ARROW) dam++;
368  | 			    }
369  | 			    if (bigmonst(youmonst.data)) hitv++;
370  | 			    hitv += 8 + singleobj->spe;
371  | 			    if (dam < 1) dam = 1;
372  | 			    hitu = thitu(hitv, dam, singleobj, (char *)0);
373  | 		    }
374  | 		    if (hitu && singleobj->opoisoned) {
375  | 			char onmbuf[BUFSZ], knmbuf[BUFSZ];
376  | 			struct obj otmp;
377  | 			unsigned save_ocknown;
378  | 
379  | 			/* [see thitu()'s handling of `name'] */
380  | 			Strcpy(onmbuf, xname(singleobj));
381  | 			otmp = *singleobj;
382  | 			save_ocknown = objects[otmp.otyp].oc_name_known;
383  | 			otmp.known = otmp.dknown = 1;
384  | 			otmp.bknown = otmp.rknown = otmp.greased = 0;
385  | 			/* "poisoned by poisoned <obj>" would be redundant */
386  | 			otmp.opoisoned = 0;
387  | 			objects[otmp.otyp].oc_name_known = 1;
388  | 			Strcpy(knmbuf, xname(&otmp));
389  | 			poisoned(onmbuf, A_STR, knmbuf, 10);
390  | 			objects[otmp.otyp].oc_name_known = save_ocknown;
391  | 		    }
392  | 		    if(hitu && (singleobj->otyp == CREAM_PIE ||
393  | 				 singleobj->otyp == BLINDING_VENOM)) {
394  | 			blindinc = rnd(25);
395  | 			if(singleobj->otyp == CREAM_PIE) {
396  | 			    if(!Blind) pline("Yecch!  You've been creamed.");
397  | 			    else	pline("There's %s sticky all over your %s.",
398  | 					    something,
399  | 					    body_part(FACE));
400  | 			} else {	/* venom in the eyes */
401  | 			    if(ublindf) /* nothing */ ;
402  | 			    else if(!Blind) pline_The("venom blinds you.");
403  | 			    else Your("%s sting.", makeplural(body_part(EYE)));
404  | 			}
405  | 		    }
406  | 		    if (hitu && singleobj->otyp == EGG) {
407  | 			if (!Stone_resistance
408  | 				&& !(poly_when_stoned(youmonst.data) &&
409  | 				    polymon(PM_STONE_GOLEM)))
410  | 			    Stoned = 5;
411  | 			    killer = (char *) 0;
412  | 		    }
413  | 		    stop_occupation();
414  | 		    if (hitu || !range) {
415  | 			(void) drop_throw(singleobj, hitu, u.ux, u.uy);
416  | 			break;
417  | 		    }
418  | 		} else if (!range	/* reached end of path */
419  | 			/* missile hits edge of screen */
420  | 			|| !isok(bhitpos.x+dx,bhitpos.y+dy)
421  | 			/* missile hits the wall */
422  | 			|| IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ)
423  | #ifdef SINKS
424  | 			/* Thrown objects "sink" */
425  | 			|| IS_SINK(levl[bhitpos.x][bhitpos.y].typ)
426  | #endif
427  | 								) {
428  | 		    (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
429  | 		    break;
430  | 		}
431  | 		tmp_at(bhitpos.x, bhitpos.y);
432  | 		delay_output();
433  | 	}
434  | 	tmp_at(bhitpos.x, bhitpos.y);
435  | 	delay_output();
436  | 	tmp_at(DISP_END, 0);
437  | 	/* blindfolds, towels, & lenses keep substances out of your eyes */
438  | 	if (blindinc && !ublindf) {
439  | 		u.ucreamed += blindinc;
440  | 		make_blinded(Blinded + blindinc,FALSE);
441  | 	}
442  | }
443  | 
444  | #endif /* OVL1 */
445  | #ifdef OVLB
446  | 
447  | /* Remove an item from the monster's inventory and destroy it. */
448  | void
449  | m_useup(mon, obj)
450  | struct monst *mon;
451  | struct obj *obj;
452  | {
453  | 	if (obj->quan > 1L) {
454  | 		obj->quan--;
455  | 	} else {
456  | 		obj_extract_self(obj);
457  | 		possibly_unwield(mon);
458  | 		if (obj->owornmask) {
459  | 		    mon->misc_worn_check &= ~obj->owornmask;
460  | 		    update_mon_intrinsics(mon, obj, FALSE);
461  | 		}
462  | 		dealloc_obj(obj);
463  | 	}
464  | }
465  | 
466  | #endif /* OVLB */
467  | #ifdef OVL1
468  | 
469  | void
470  | thrwmu(mtmp)	/* monster throws item at you */
471  | register struct monst *mtmp;
472  | {
473  | 	struct obj *otmp, *mwep;
474  | 	register xchar x, y;
475  | 	boolean ispole;
476  | 	schar skill;
477  | 	int multishot = 1;
478  | 
479  | 	/* Rearranged beginning so monsters can use polearms not in a line */
480  | 	if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
481  | 	    mtmp->weapon_check = NEED_RANGED_WEAPON;
482  | 	    /* mon_wield_item resets weapon_check as appropriate */
483  | 	    if(mon_wield_item(mtmp) != 0) return;
484  | 	}
485  | 
486  | 	/* Pick a weapon */
487  | 	otmp = select_rwep(mtmp);
488  | 	if (!otmp) return;
489  | 	ispole = is_pole(otmp);
490  | 	skill = objects[otmp->otyp].oc_skill;
491  | 	mwep = MON_WEP(mtmp);		/* wielded weapon */
492  | 
493  | 	if(ispole || lined_up(mtmp)) {
494  | 		/* If you are coming toward the monster, the monster
495  | 		 * should try to soften you up with missiles.  If you are
496  | 		 * going away, you are probably hurt or running.  Give
497  | 		 * chase, but if you are getting too far away, throw.
498  | 		 */
499  | 		x = mtmp->mx;
500  | 		y = mtmp->my;
501  | 		if(ispole || !URETREATING(x,y) ||
502  | 		   !rn2(BOLT_LIM-distmin(x,y,mtmp->mux,mtmp->muy)))
503  | 		{
504  | 		    const char *verb = "throws";
505  | 
506  | 		    if (otmp->otyp == ARROW
507  | 			|| otmp->otyp == ELVEN_ARROW
508  | 			|| otmp->otyp == ORCISH_ARROW
509  | 			|| otmp->otyp == YA
510  | 			|| otmp->otyp == CROSSBOW_BOLT) verb = "shoots";
511  | 		    if (ispole) {
512  | 			if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <=
513  | 				POLE_LIM && couldsee(mtmp->mx, mtmp->my))
514  | 			    verb = "thrusts";
515  | 			else return; /* Out of range, or intervening wall */
516  | 		    }
517  | 
518  | 		    if (canseemon(mtmp)) {
519  | 			pline("%s %s %s!", Monnam(mtmp), verb,
520  | 			      obj_is_pname(otmp) ?
521  | 			      the(singular(otmp, xname)) :
522  | 			      an(singular(otmp, xname)));
523  | 		    }
524  | 
525  | 			/* Use a pole */
526  | 			if (ispole) {
527  | 				int dam = dmgval(otmp, &youmonst);
528  | 				int hitv = 3 - distmin(u.ux,u.uy, mtmp->mx,mtmp->my);
529  | 
530  | 				if (hitv < -4) hitv = -4;
531  | 				if (bigmonst(youmonst.data)) hitv++;
532  | 				hitv += 8 + otmp->spe;
533  | 				if (dam < 1) dam = 1;
534  | 				(void) thitu(hitv, dam, otmp, (char *)0);
535  | 
536  | 				return;
537  | 			}
538  | 
539  | 		    /* Multishot calculations */
540  | 		    if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER ||
541  | 				skill == -P_DART || skill == -P_SHURIKEN) &&
542  | 			    !mtmp->mconf) {
543  | 			/* Assumes lords are skilled, princes are expert */
544  | 			if (is_lord(mtmp->data)) multishot++;
545  | 			if (is_prince(mtmp->data)) multishot += 2;
546  | 
547  | 			switch (monsndx(mtmp->data)) {
548  | 			case PM_RANGER:
549  | 			    multishot++;
550  | 			    break;
551  | 			case PM_ROGUE:
552  | 			    if (skill == P_DAGGER) multishot++;
553  | 			    break;
554  | 			case PM_SAMURAI:
555  | 			    if (otmp->otyp == YA && mwep &&
556  | 				    mwep->otyp == YUMI) multishot++;
557  | 			    break;
558  | 			default:
559  | 			    break;
560  | 			}
561  | 			{	/* racial bonus */
562  | 			    if (is_elf(mtmp->data) &&
563  | 				    otmp->otyp == ELVEN_ARROW &&
564  | 				    mwep && mwep->otyp == ELVEN_BOW)
565  | 				multishot++;
566  | 			    else if (is_orc(mtmp->data) &&
567  | 				    otmp->otyp == ORCISH_ARROW &&
568  | 				    mwep && mwep->otyp == ORCISH_BOW)
569  | 				multishot++;
570  | 			}
571  | 		    }
572  | 		    if (otmp->quan < multishot) multishot = (int)otmp->quan;
573  | 		    if (multishot < 1) multishot = 1;
574  | 		    else multishot = rnd(multishot);
575  | 		    while (multishot-- > 0)
576  | 			m_throw(mtmp, mtmp->mx, mtmp->my,
577  | 				sgn(tbx), sgn(tby),
578  | 				distmin(mtmp->mx, mtmp->my,
579  | 					mtmp->mux, mtmp->muy),
580  | 				otmp);
581  | 		    nomul(0);
582  | 		    return;
583  | 		}
584  | 	}
585  | }
586  | 
587  | #endif /* OVL1 */
588  | #ifdef OVLB
589  | 
590  | int
591  | spitmu(mtmp, mattk)		/* monster spits substance at you */
592  | register struct monst *mtmp;
593  | register struct attack *mattk;
594  | {
595  | 	register struct obj *otmp;
596  | 
597  | 	if(mtmp->mcan) {
598  | 
599  | 	    if(flags.soundok)
600  | 		pline("A dry rattle comes from %s throat.",
601  | 		                      s_suffix(mon_nam(mtmp)));
602  | 	    return 0;
603  | 	}
604  | 	if(lined_up(mtmp)) {
605  | 		switch (mattk->adtyp) {
606  | 		    case AD_BLND:
607  | 		    case AD_DRST:
608  | 			otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
609  | 			break;
610  | 		    default:
611  | 			impossible("bad attack type in spitmu");
612  | 				/* fall through */
613  | 		    case AD_ACID:
614  | 			otmp = mksobj(ACID_VENOM, TRUE, FALSE);
615  | 			break;
616  | 		}
617  | 		if(!rn2(BOLT_LIM-distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy))) {
618  | 		    if (canseemon(mtmp))
619  | 			pline("%s spits venom!", Monnam(mtmp));
620  | 		    m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
621  | 			distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy), otmp);
622  | 		    nomul(0);
623  | 		    return 0;
624  | 		}
625  | 	}
626  | 	return 0;
627  | }
628  | 
629  | #endif /* OVLB */
630  | #ifdef OVL1
631  | 
632  | int
633  | breamu(mtmp, mattk)			/* monster breathes at you (ranged) */
634  | 	register struct monst *mtmp;
635  | 	register struct attack  *mattk;
636  | {
637  | 	/* if new breath types are added, change AD_ACID to max type */
638  | 	int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp ;
639  | 
640  | 	if(lined_up(mtmp)) {
641  | 
642  | 	    if(mtmp->mcan) {
643  | 		if(flags.soundok) {
644  | 		    if(canseemon(mtmp))
645  | 			pline("%s coughs.", Monnam(mtmp));
646  | 		    else
647  | 			You_hear("a cough.");
648  | 		}
649  | 		return(0);
650  | 	    }
651  | 	    if(!mtmp->mspec_used && rn2(3)) {
652  | 
653  | 		if((typ >= AD_MAGM) && (typ <= AD_ACID)) {
654  | 
655  | 		    if(canseemon(mtmp))
656  | 			pline("%s breathes %s!", Monnam(mtmp),
657  | 			      breathwep[typ-1]);
658  | 		    buzz((int) (-20 - (typ-1)), (int)mattk->damn,
659  | 			 mtmp->mx, mtmp->my, sgn(tbx), sgn(tby));
660  | 		    nomul(0);
661  | 		    /* breath runs out sometimes. Also, give monster some
662  | 		     * cunning; don't breath if the player fell asleep.
663  | 		     */
664  | 		    if(!rn2(3))
665  | 			mtmp->mspec_used = 10+rn2(20);
666  | 		    if(typ == AD_SLEE && !Sleep_resistance)
667  | 			mtmp->mspec_used += rnd(20);
668  | 		} else impossible("Breath weapon %d used", typ-1);
669  | 	    }
670  | 	}
671  | 	return(1);
672  | }
673  | 
674  | boolean
675  | linedup(ax, ay, bx, by)
676  | register xchar ax, ay, bx, by;
677  | {
678  | 	tbx = ax - bx;	/* These two values are set for use */
679  | 	tby = ay - by;	/* after successful return.	    */
680  | 
681  | 	/* sometimes displacement makes a monster think that you're at its
682  | 	   own location; prevent it from throwing and zapping in that case */
683  | 	if (!tbx && !tby) return FALSE;
684  | 
685  | 	if((!tbx || !tby || abs(tbx) == abs(tby)) /* straight line or diagonal */
686  | 	   && distmin(tbx, tby, 0, 0) < BOLT_LIM) {
687  | 	    if(ax == u.ux && ay == u.uy) return((boolean)(couldsee(bx,by)));
688  | 	    else if(clear_path(ax,ay,bx,by)) return TRUE;
689  | 	}
690  | 	return FALSE;
691  | }
692  | 
693  | boolean
694  | lined_up(mtmp)		/* is mtmp in position to use ranged attack? */
695  | 	register struct monst *mtmp;
696  | {
697  | 	return(linedup(mtmp->mux,mtmp->muy,mtmp->mx,mtmp->my));
698  | }
699  | 
700  | #endif /* OVL1 */
701  | #ifdef OVL0
702  | 
703  | /* Check if a monster is carrying a particular item.
704  |  */
705  | struct obj *
706  | m_carrying(mtmp, type)
707  | struct monst *mtmp;
708  | int type;
709  | {
710  | 	register struct obj *otmp;
711  | 
712  | 	for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
713  | 		if(otmp->otyp == type)
714  | 			return(otmp);
715  | 	return((struct obj *) 0);
716  | }
717  | 
718  | #endif /* OVL0 */
719  | 
720  | /*mthrowu.c*/