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*/