more angry god vs pacifist conduct
[aNetHack.git] / src / mthrowu.c
blobfbf9d08cf22aadf69e43da3ebe6965426ff7da73
1 /* NetHack 3.6 mthrowu.c $NHDT-Date: 1446887531 2015/11/07 09:12:11 $ $NHDT-Branch: master $:$NHDT-Revision: 1.63 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
7 STATIC_DCL int FDECL(drop_throw, (struct obj *, BOOLEAN_P, int, int));
9 #define URETREATING(x, y) \
10 (distmin(u.ux, u.uy, x, y) > distmin(u.ux0, u.uy0, x, y))
12 #define POLE_LIM 5 /* How far monsters can use pole-weapons */
15 * Keep consistent with breath weapons in zap.c, and AD_* in monattk.h.
17 STATIC_OVL NEARDATA const char *breathwep[] = {
18 "fragments", "fire", "frost", "sleep gas", "a disintegration blast",
19 "lightning", "poison gas", "acid", "strange breath #8",
20 "strange breath #9"
23 extern boolean notonhead; /* for long worms */
25 /* hero is hit by something other than a monster */
26 int
27 thitu(tlev, dam, obj, name)
28 int tlev, dam;
29 struct obj *obj;
30 const char *name; /* if null, then format `obj' */
32 const char *onm, *knm;
33 boolean is_acid;
34 int kprefix = KILLED_BY_AN;
35 char onmbuf[BUFSZ], knmbuf[BUFSZ];
37 if (!name) {
38 if (!obj)
39 panic("thitu: name & obj both null?");
40 name =
41 strcpy(onmbuf, (obj->quan > 1L) ? doname(obj) : mshot_xname(obj));
42 knm = strcpy(knmbuf, killer_xname(obj));
43 kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */
44 } else {
45 knm = name;
46 /* [perhaps ought to check for plural here to] */
47 if (!strncmpi(name, "the ", 4) || !strncmpi(name, "an ", 3)
48 || !strncmpi(name, "a ", 2))
49 kprefix = KILLED_BY;
51 onm = (obj && obj_is_pname(obj)) ? the(name) : (obj && obj->quan > 1L)
52 ? name
53 : an(name);
54 is_acid = (obj && obj->otyp == ACID_VENOM);
56 if (u.uac + tlev <= rnd(20)) {
57 if (Blind || !flags.verbose)
58 pline("It misses.");
59 else
60 You("are almost hit by %s.", onm);
61 return 0;
62 } else {
63 if (Blind || !flags.verbose)
64 You("are hit%s", exclam(dam));
65 else
66 You("are hit by %s%s", onm, exclam(dam));
68 if (obj && objects[obj->otyp].oc_material == SILVER && Hate_silver) {
69 /* extra damage already applied by dmgval() */
70 pline_The("silver sears your flesh!");
71 exercise(A_CON, FALSE);
73 if (is_acid && Acid_resistance)
74 pline("It doesn't seem to hurt you.");
75 else {
76 if (is_acid)
77 pline("It burns!");
78 losehp(dam, knm, kprefix); /* acid damage */
79 exercise(A_STR, FALSE);
81 return 1;
85 /* Be sure this corresponds with what happens to player-thrown objects in
86 * dothrow.c (for consistency). --KAA
87 * Returns 0 if object still exists (not destroyed).
89 STATIC_OVL int
90 drop_throw(obj, ohit, x, y)
91 register struct obj *obj;
92 boolean ohit;
93 int x, y;
95 int retvalu = 1;
96 int create;
97 struct monst *mtmp;
98 struct trap *t;
100 if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS
101 || (ohit && obj->otyp == EGG))
102 create = 0;
103 else if (ohit && (is_multigen(obj) || obj->otyp == ROCK))
104 create = !rn2(3);
105 else
106 create = 1;
108 if (create
109 && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) && (t = t_at(x, y))
110 && ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT)))) {
111 int objgone = 0;
113 if (down_gate(x, y) != -1)
114 objgone = ship_object(obj, x, y, FALSE);
115 if (!objgone) {
116 if (!flooreffects(obj, x, y,
117 "fall")) { /* don't double-dip on damage */
118 place_object(obj, x, y);
119 if (!mtmp && x == u.ux && y == u.uy)
120 mtmp = &youmonst;
121 if (mtmp && ohit)
122 passive_obj(mtmp, obj, (struct attack *) 0);
123 stackobj(obj);
124 retvalu = 0;
127 } else
128 obfree(obj, (struct obj *) 0);
129 return retvalu;
132 /* an object launched by someone/thing other than player attacks a monster;
133 return 1 if the object has stopped moving (hit or its range used up) */
135 ohitmon(mtmp, otmp, range, verbose)
136 struct monst *mtmp; /* accidental target, located at <bhitpos.x,.y> */
137 struct obj *otmp; /* missile; might be destroyed by drop_throw */
138 int range; /* how much farther will object travel if it misses;
139 use -1 to signify to keep going even after hit,
140 unless it's gone (used for rolling_boulder_traps) */
141 boolean verbose; /* give message(s) even when you can't see what happened */
143 int damage, tmp;
144 boolean vis, ismimic;
145 int objgone = 1;
147 notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
148 ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER;
149 vis = cansee(bhitpos.x, bhitpos.y);
151 tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE);
152 if (tmp < rnd(20)) {
153 if (!ismimic) {
154 if (vis)
155 miss(distant_name(otmp, mshot_xname), mtmp);
156 else if (verbose)
157 pline("It is missed.");
159 if (!range) { /* Last position; object drops */
160 (void) drop_throw(otmp, 0, mtmp->mx, mtmp->my);
161 return 1;
163 } else if (otmp->oclass == POTION_CLASS) {
164 if (ismimic)
165 seemimic(mtmp);
166 mtmp->msleeping = 0;
167 if (vis)
168 otmp->dknown = 1;
169 potionhit(mtmp, otmp, FALSE);
170 return 1;
171 } else {
172 damage = dmgval(otmp, mtmp);
173 if (otmp->otyp == ACID_VENOM && resists_acid(mtmp))
174 damage = 0;
175 if (ismimic)
176 seemimic(mtmp);
177 mtmp->msleeping = 0;
178 if (vis)
179 hit(distant_name(otmp, mshot_xname), mtmp, exclam(damage));
180 else if (verbose)
181 pline("%s is hit%s", Monnam(mtmp), exclam(damage));
183 if (otmp->opoisoned && is_poisonable(otmp)) {
184 if (resists_poison(mtmp)) {
185 if (vis)
186 pline_The("poison doesn't seem to affect %s.",
187 mon_nam(mtmp));
188 } else {
189 if (rn2(30)) {
190 damage += rnd(6);
191 } else {
192 if (vis)
193 pline_The("poison was deadly...");
194 damage = mtmp->mhp;
198 if (objects[otmp->otyp].oc_material == SILVER
199 && mon_hates_silver(mtmp)) {
200 if (vis)
201 pline_The("silver sears %s flesh!", s_suffix(mon_nam(mtmp)));
202 else if (verbose)
203 pline("Its flesh is seared!");
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)
212 pline_The("acid burns %s!", mon_nam(mtmp));
213 else if (verbose)
214 pline("It is burned!");
217 mtmp->mhp -= damage;
218 if (mtmp->mhp < 1) {
219 if (vis || verbose)
220 pline("%s is %s!", Monnam(mtmp),
221 (nonliving(mtmp->data) || is_vampshifter(mtmp)
222 || !canspotmon(mtmp))
223 ? "destroyed"
224 : "killed");
225 /* don't blame hero for unknown rolling boulder trap */
226 if (!context.mon_moving
227 && (otmp->otyp != BOULDER || range >= 0 || otmp->otrapped))
228 xkilled(mtmp, XKILL_NOMSG);
229 else
230 mondied(mtmp);
233 if (can_blnd((struct monst *) 0, mtmp,
234 (uchar) (otmp->otyp == BLINDING_VENOM ? AT_SPIT
235 : AT_WEAP),
236 otmp)) {
237 if (vis && mtmp->mcansee)
238 pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp)));
239 mtmp->mcansee = 0;
240 tmp = (int) mtmp->mblinded + rnd(25) + 20;
241 if (tmp > 127)
242 tmp = 127;
243 mtmp->mblinded = tmp;
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;
251 return 1;
253 return 0;
256 void
257 m_throw(mon, x, y, dx, dy, range, obj)
258 struct monst *mon; /* launching monster */
259 int x, y, dx, dy, range; /* launch point, direction, and range */
260 struct obj *obj; /* missile (or stack providing it) */
262 struct monst *mtmp;
263 struct obj *singleobj;
264 char sym = obj->oclass;
265 int hitu, oldumort, blindinc = 0;
267 bhitpos.x = x;
268 bhitpos.y = y;
269 notonhead = FALSE; /* reset potentially stale value */
271 if (obj->quan == 1L) {
273 * Remove object from minvent. This cannot be done later on;
274 * what if the player dies before then, leaving the monster
275 * with 0 daggers? (This caused the infamous 2^32-1 orcish
276 * dagger bug).
278 * VENOM is not in minvent - it should already be OBJ_FREE.
279 * The extract below does nothing.
282 /* not possibly_unwield, which checks the object's */
283 /* location, not its existence */
284 if (MON_WEP(mon) == obj)
285 setmnotwielded(mon, obj);
286 obj_extract_self(obj);
287 singleobj = obj;
288 obj = (struct obj *) 0;
289 } else {
290 singleobj = splitobj(obj, 1L);
291 obj_extract_self(singleobj);
294 singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */
296 if ((singleobj->cursed || singleobj->greased) && (dx || dy) && !rn2(7)) {
297 if (canseemon(mon) && flags.verbose) {
298 if (is_ammo(singleobj))
299 pline("%s misfires!", Monnam(mon));
300 else
301 pline("%s as %s throws it!", Tobjnam(singleobj, "slip"),
302 mon_nam(mon));
304 dx = rn2(3) - 1;
305 dy = rn2(3) - 1;
306 /* check validity of new direction */
307 if (!dx && !dy) {
308 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
309 return;
313 /* pre-check for doors, walls and boundaries.
314 Also need to pre-check for bars regardless of direction;
315 the random chance for small objects hitting bars is
316 skipped when reaching them at point blank range */
317 if (!isok(bhitpos.x + dx, bhitpos.y + dy)
318 || IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ)
319 || closed_door(bhitpos.x + dx, bhitpos.y + dy)
320 || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS
321 && hits_bars(&singleobj,
322 bhitpos.x, bhitpos.y,
323 bhitpos.x + dx, bhitpos.y + dy, 0, 0))) {
324 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
325 return;
328 /* Note: drop_throw may destroy singleobj. Since obj must be destroyed
329 * early to avoid the dagger bug, anyone who modifies this code should
330 * be careful not to use either one after it's been freed.
332 if (sym)
333 tmp_at(DISP_FLASH, obj_to_glyph(singleobj));
334 while (range-- > 0) { /* Actually the loop is always exited by break */
335 bhitpos.x += dx;
336 bhitpos.y += dy;
337 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
338 if (ohitmon(mtmp, singleobj, range, TRUE))
339 break;
340 } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
341 if (multi)
342 nomul(0);
344 if (singleobj->oclass == GEM_CLASS
345 && singleobj->otyp <= LAST_GEM + 9 /* 9 glass colors */
346 && is_unicorn(youmonst.data)) {
347 if (singleobj->otyp > LAST_GEM) {
348 You("catch the %s.", xname(singleobj));
349 You("are not interested in %s junk.",
350 s_suffix(mon_nam(mon)));
351 makeknown(singleobj->otyp);
352 dropy(singleobj);
353 } else {
354 You(
355 "accept %s gift in the spirit in which it was intended.",
356 s_suffix(mon_nam(mon)));
357 (void) hold_another_object(
358 singleobj, "You catch, but drop, %s.",
359 xname(singleobj), "You catch:");
361 break;
363 if (singleobj->oclass == POTION_CLASS) {
364 if (!Blind)
365 singleobj->dknown = 1;
366 potionhit(&youmonst, singleobj, FALSE);
367 break;
369 oldumort = u.umortality;
370 switch (singleobj->otyp) {
371 int dam, hitv;
372 case EGG:
373 if (!touch_petrifies(&mons[singleobj->corpsenm])) {
374 impossible("monster throwing egg type %d",
375 singleobj->corpsenm);
376 hitu = 0;
377 break;
379 /* fall through */
380 case CREAM_PIE:
381 case BLINDING_VENOM:
382 hitu = thitu(8, 0, singleobj, (char *) 0);
383 break;
384 default:
385 dam = dmgval(singleobj, &youmonst);
386 hitv = 3 - distmin(u.ux, u.uy, mon->mx, mon->my);
387 if (hitv < -4)
388 hitv = -4;
389 if (is_elf(mon->data)
390 && objects[singleobj->otyp].oc_skill == P_BOW) {
391 hitv++;
392 if (MON_WEP(mon) && MON_WEP(mon)->otyp == ELVEN_BOW)
393 hitv++;
394 if (singleobj->otyp == ELVEN_ARROW)
395 dam++;
397 if (bigmonst(youmonst.data))
398 hitv++;
399 hitv += 8 + singleobj->spe;
400 if (dam < 1)
401 dam = 1;
402 hitu = thitu(hitv, dam, singleobj, (char *) 0);
404 if (hitu && singleobj->opoisoned && is_poisonable(singleobj)) {
405 char onmbuf[BUFSZ], knmbuf[BUFSZ];
407 Strcpy(onmbuf, xname(singleobj));
408 Strcpy(knmbuf, killer_xname(singleobj));
409 poisoned(onmbuf, A_STR, knmbuf,
410 /* if damage triggered life-saving,
411 poison is limited to attrib loss */
412 (u.umortality > oldumort) ? 0 : 10, TRUE);
414 if (hitu && can_blnd((struct monst *) 0, &youmonst,
415 (uchar) (singleobj->otyp == BLINDING_VENOM
416 ? AT_SPIT
417 : AT_WEAP),
418 singleobj)) {
419 blindinc = rnd(25);
420 if (singleobj->otyp == CREAM_PIE) {
421 if (!Blind)
422 pline("Yecch! You've been creamed.");
423 else
424 pline("There's %s sticky all over your %s.",
425 something, body_part(FACE));
426 } else if (singleobj->otyp == BLINDING_VENOM) {
427 const char *eyes = body_part(EYE);
429 if (eyecount(youmonst.data) != 1)
430 eyes = makeplural(eyes);
431 /* venom in the eyes */
432 if (!Blind)
433 pline_The("venom blinds you.");
434 else
435 Your("%s %s.", eyes, vtense(eyes, "sting"));
438 if (hitu && singleobj->otyp == EGG) {
439 if (!Stoned && !Stone_resistance
440 && !(poly_when_stoned(youmonst.data)
441 && polymon(PM_STONE_GOLEM))) {
442 make_stoned(5L, (char *) 0, KILLED_BY, "");
445 stop_occupation();
446 if (hitu || !range) {
447 (void) drop_throw(singleobj, hitu, u.ux, u.uy);
448 break;
451 if (!range /* reached end of path */
452 /* missile hits edge of screen */
453 || !isok(bhitpos.x + dx, bhitpos.y + dy)
454 /* missile hits the wall */
455 || IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ)
456 /* missile hit closed door */
457 || closed_door(bhitpos.x + dx, bhitpos.y + dy)
458 /* missile might hit iron bars */
459 || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS
460 && hits_bars(&singleobj,
461 bhitpos.x, bhitpos.y,
462 bhitpos.x + dx, bhitpos.y + dy,
463 !rn2(5), 0))
464 /* Thrown objects "sink" */
465 || IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) {
466 if (singleobj) /* hits_bars might have destroyed it */
467 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
468 break;
470 tmp_at(bhitpos.x, bhitpos.y);
471 delay_output();
473 tmp_at(bhitpos.x, bhitpos.y);
474 delay_output();
475 tmp_at(DISP_END, 0);
477 if (blindinc) {
478 u.ucreamed += blindinc;
479 make_blinded(Blinded + (long) blindinc, FALSE);
480 if (!Blind)
481 Your1(vision_clears);
485 /* remove an entire item from a monster's inventory; destroy that item */
486 void
487 m_useupall(mon, obj)
488 struct monst *mon;
489 struct obj *obj;
491 obj_extract_self(obj);
492 if (obj->owornmask) {
493 if (obj == MON_WEP(mon))
494 mwepgone(mon);
495 mon->misc_worn_check &= ~obj->owornmask;
496 update_mon_intrinsics(mon, obj, FALSE, FALSE);
497 obj->owornmask = 0L;
499 obfree(obj, (struct obj *) 0);
502 /* remove one instance of an item from a monster's inventory */
503 void
504 m_useup(mon, obj)
505 struct monst *mon;
506 struct obj *obj;
508 if (obj->quan > 1L) {
509 obj->quan--;
510 obj->owt = weight(obj);
511 } else {
512 m_useupall(mon, obj);
516 /* monster attempts ranged weapon attack against player */
517 void
518 thrwmu(mtmp)
519 struct monst *mtmp;
521 struct obj *otmp, *mwep;
522 xchar x, y;
523 int multishot;
524 const char *onm;
526 /* Rearranged beginning so monsters can use polearms not in a line */
527 if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
528 mtmp->weapon_check = NEED_RANGED_WEAPON;
529 /* mon_wield_item resets weapon_check as appropriate */
530 if (mon_wield_item(mtmp) != 0)
531 return;
534 /* Pick a weapon */
535 otmp = select_rwep(mtmp);
536 if (!otmp)
537 return;
539 if (is_pole(otmp)) {
540 int dam, hitv;
542 if (otmp != MON_WEP(mtmp))
543 return; /* polearm must be wielded */
544 if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) > POLE_LIM
545 || !couldsee(mtmp->mx, mtmp->my))
546 return; /* Out of range, or intervening wall */
548 if (canseemon(mtmp)) {
549 onm = xname(otmp);
550 pline("%s thrusts %s.", Monnam(mtmp),
551 obj_is_pname(otmp) ? the(onm) : an(onm));
554 dam = dmgval(otmp, &youmonst);
555 hitv = 3 - distmin(u.ux, u.uy, mtmp->mx, mtmp->my);
556 if (hitv < -4)
557 hitv = -4;
558 if (bigmonst(youmonst.data))
559 hitv++;
560 hitv += 8 + otmp->spe;
561 if (dam < 1)
562 dam = 1;
564 (void) thitu(hitv, dam, otmp, (char *) 0);
565 stop_occupation();
566 return;
569 x = mtmp->mx;
570 y = mtmp->my;
571 /* If you are coming toward the monster, the monster
572 * should try to soften you up with missiles. If you are
573 * going away, you are probably hurt or running. Give
574 * chase, but if you are getting too far away, throw.
576 if (!lined_up(mtmp)
577 || (URETREATING(x, y)
578 && rn2(BOLT_LIM - distmin(x, y, mtmp->mux, mtmp->muy))))
579 return;
581 mwep = MON_WEP(mtmp); /* wielded weapon */
583 /* Multishot calculations */
584 multishot = 1;
585 if (otmp->quan > 1L /* no point checking if there's only 1 */
586 /* ammo requires corresponding launcher be wielded */
587 && (is_ammo(otmp)
588 ? matching_launcher(otmp, mwep)
589 /* otherwise any stackable (non-ammo) weapon */
590 : otmp->oclass == WEAPON_CLASS)
591 && !mtmp->mconf) {
592 int skill = (int) objects[otmp->otyp].oc_skill;
594 /* Assumes lords are skilled, princes are expert */
595 if (is_prince(mtmp->data))
596 multishot += 2;
597 else if (is_lord(mtmp->data))
598 multishot++;
599 /* fake players treated as skilled (regardless of role limits) */
600 else if (is_mplayer(mtmp->data))
601 multishot++;
603 /* class bonus */
604 switch (monsndx(mtmp->data)) {
605 case PM_MONK:
606 if (skill == -P_SHURIKEN)
607 multishot++;
608 break;
609 case PM_RANGER:
610 multishot++;
611 break;
612 case PM_ROGUE:
613 if (skill == P_DAGGER)
614 multishot++;
615 break;
616 case PM_NINJA:
617 if (skill == -P_SHURIKEN || skill == -P_DART)
618 multishot++;
619 /*FALLTHRU*/
620 case PM_SAMURAI:
621 if (otmp->otyp == YA && mwep && mwep->otyp == YUMI)
622 multishot++;
623 break;
624 default:
625 break;
627 /* racial bonus */
628 if ((is_elf(mtmp->data) && otmp->otyp == ELVEN_ARROW && mwep
629 && mwep->otyp == ELVEN_BOW)
630 || (is_orc(mtmp->data) && otmp->otyp == ORCISH_ARROW && mwep
631 && mwep->otyp == ORCISH_BOW))
632 multishot++;
634 multishot = rnd(multishot);
635 if ((long) multishot > otmp->quan)
636 multishot = (int) otmp->quan;
639 if (canseemon(mtmp)) {
640 char onmbuf[BUFSZ];
642 if (multishot > 1) {
643 /* "N arrows"; multishot > 1 implies otmp->quan > 1, so
644 xname()'s result will already be pluralized */
645 Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
646 onm = onmbuf;
647 } else {
648 /* "an arrow" */
649 onm = singular(otmp, xname);
650 onm = obj_is_pname(otmp) ? the(onm) : an(onm);
652 m_shot.s = ammo_and_launcher(otmp, mwep) ? TRUE : FALSE;
653 pline("%s %s %s!", Monnam(mtmp), m_shot.s ? "shoots" : "throws", onm);
654 m_shot.o = otmp->otyp;
655 } else {
656 m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */
659 m_shot.n = multishot;
660 for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) {
661 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
662 distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
663 /* conceptually all N missiles are in flight at once, but
664 if mtmp gets killed (shot kills adjacent gas spore and
665 triggers explosion, perhaps), inventory will be dropped
666 and otmp might go away via merging into another stack */
667 if (mtmp->mhp <= 0 && m_shot.i < m_shot.n) {
668 /* cancel pending shots (ought to give a message here since
669 we gave one above about throwing/shooting N missiles) */
670 break; /* endmultishot(FALSE); */
673 m_shot.n = m_shot.i = 0;
674 m_shot.o = STRANGE_OBJECT;
675 m_shot.s = FALSE;
677 nomul(0);
680 /* monster spits substance at you */
682 spitmu(mtmp, mattk)
683 struct monst *mtmp;
684 struct attack *mattk;
686 struct obj *otmp;
688 if (mtmp->mcan) {
689 if (!Deaf)
690 pline("A dry rattle comes from %s throat.",
691 s_suffix(mon_nam(mtmp)));
692 return 0;
694 if (lined_up(mtmp)) {
695 switch (mattk->adtyp) {
696 case AD_BLND:
697 case AD_DRST:
698 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
699 break;
700 default:
701 impossible("bad attack type in spitmu");
702 /* fall through */
703 case AD_ACID:
704 otmp = mksobj(ACID_VENOM, TRUE, FALSE);
705 break;
707 if (!rn2(BOLT_LIM
708 - distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy))) {
709 if (canseemon(mtmp))
710 pline("%s spits venom!", Monnam(mtmp));
711 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
712 distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
713 nomul(0);
714 return 0;
715 } else {
716 obj_extract_self(otmp);
717 obfree(otmp, (struct obj *) 0);
720 return 0;
723 /* monster breathes at you (ranged) */
725 breamu(mtmp, mattk)
726 struct monst *mtmp;
727 struct attack *mattk;
729 /* if new breath types are added, change AD_ACID to max type */
730 int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp;
732 if (lined_up(mtmp)) {
733 if (mtmp->mcan) {
734 if (!Deaf) {
735 if (canseemon(mtmp))
736 pline("%s coughs.", Monnam(mtmp));
737 else
738 You_hear("a cough.");
740 return 0;
742 if (!mtmp->mspec_used && rn2(3)) {
743 if ((typ >= AD_MAGM) && (typ <= AD_ACID)) {
744 if (canseemon(mtmp))
745 pline("%s breathes %s!", Monnam(mtmp),
746 breathwep[typ - 1]);
747 buzz((int) (-20 - (typ - 1)), (int) mattk->damn, mtmp->mx,
748 mtmp->my, sgn(tbx), sgn(tby));
749 nomul(0);
750 /* breath runs out sometimes. Also, give monster some
751 * cunning; don't breath if the player fell asleep.
753 if (!rn2(3))
754 mtmp->mspec_used = 10 + rn2(20);
755 if (typ == AD_SLEE && !Sleep_resistance)
756 mtmp->mspec_used += rnd(20);
757 } else
758 impossible("Breath weapon %d used", typ - 1);
761 return 1;
764 boolean
765 linedup(ax, ay, bx, by, boulderhandling)
766 register xchar ax, ay, bx, by;
767 int boulderhandling; /* 0=block, 1=ignore, 2=conditionally block */
769 int dx, dy, boulderspots;
771 /* These two values are set for use after successful return. */
772 tbx = ax - bx;
773 tby = ay - by;
775 /* sometimes displacement makes a monster think that you're at its
776 own location; prevent it from throwing and zapping in that case */
777 if (!tbx && !tby)
778 return FALSE;
780 if ((!tbx || !tby || abs(tbx) == abs(tby)) /* straight line or diagonal */
781 && distmin(tbx, tby, 0, 0) < BOLT_LIM) {
782 if ((ax == u.ux && ay == u.uy) ? (boolean) couldsee(bx, by)
783 : clear_path(ax, ay, bx, by))
784 return TRUE;
785 /* don't have line of sight, but might still be lined up
786 if that lack of sight is due solely to boulders */
787 if (boulderhandling == 0)
788 return FALSE;
789 dx = sgn(ax - bx), dy = sgn(ay - by);
790 boulderspots = 0;
791 do {
792 /* <bx,by> is guaranteed to eventually converge with <ax,ay> */
793 bx += dx, by += dy;
794 if (IS_ROCK(levl[bx][by].typ) || closed_door(bx, by))
795 return FALSE;
796 if (sobj_at(BOULDER, bx, by))
797 ++boulderspots;
798 } while (bx != ax || by != ay);
799 /* reached target position without encountering obstacle */
800 if (boulderhandling == 1 || rn2(2 + boulderspots) < 2)
801 return TRUE;
803 return FALSE;
806 /* is mtmp in position to use ranged attack? */
807 boolean
808 lined_up(mtmp)
809 register struct monst *mtmp;
811 boolean ignore_boulders;
813 /* hero concealment usually trumps monst awareness of being lined up */
814 if (Upolyd && rn2(25)
815 && (u.uundetected || (youmonst.m_ap_type != M_AP_NOTHING
816 && youmonst.m_ap_type != M_AP_MONSTER)))
817 return FALSE;
819 ignore_boulders = (throws_rocks(mtmp->data)
820 || m_carrying(mtmp, WAN_STRIKING));
821 return linedup(mtmp->mux, mtmp->muy, mtmp->mx, mtmp->my,
822 ignore_boulders ? 1 : 2);
825 /* check if a monster is carrying a particular item */
826 struct obj *
827 m_carrying(mtmp, type)
828 struct monst *mtmp;
829 int type;
831 register struct obj *otmp;
833 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
834 if (otmp->otyp == type)
835 return otmp;
836 return (struct obj *) 0;
839 void
840 hit_bars(objp, objx, objy, barsx, barsy, your_fault, from_invent)
841 struct obj **objp; /* *objp will be set to NULL if object breaks */
842 int objx, objy, barsx, barsy;
843 boolean your_fault, from_invent;
845 struct obj *otmp = *objp;
846 int obj_type = otmp->otyp;
847 boolean unbreakable = (levl[barsx][barsy].wall_info & W_NONDIGGABLE) != 0;
849 if (your_fault
850 ? hero_breaks(otmp, objx, objy, from_invent)
851 : breaks(otmp, objx, objy)) {
852 *objp = 0; /* object is now gone */
853 /* breakage makes its own noises */
854 if (obj_type == POT_ACID) {
855 if (cansee(barsx, barsy) && !unbreakable)
856 pline_The("iron bars are dissolved!");
857 else
858 You_hear(Hallucination ? "angry snakes!" : "a hissing noise.");
859 if (!unbreakable)
860 dissolve_bars(barsx, barsy);
863 else if (obj_type == BOULDER || obj_type == HEAVY_IRON_BALL)
864 pline("Whang!");
865 else if (otmp->oclass == COIN_CLASS
866 || objects[obj_type].oc_material == GOLD
867 || objects[obj_type].oc_material == SILVER)
868 pline("Clink!");
869 else
870 pline("Clonk!");
873 /* TRUE iff thrown/kicked/rolled object doesn't pass through iron bars */
874 boolean
875 hits_bars(obj_p, x, y, barsx, barsy, always_hit, whodidit)
876 struct obj **obj_p; /* *obj_p will be set to NULL if object breaks */
877 int x, y, barsx, barsy;
878 int always_hit; /* caller can force a hit for items which would fit through */
879 int whodidit; /* 1==hero, 0=other, -1==just check whether it'll pass thru */
881 struct obj *otmp = *obj_p;
882 int obj_type = otmp->otyp;
883 boolean hits = always_hit;
885 if (!hits)
886 switch (otmp->oclass) {
887 case WEAPON_CLASS: {
888 int oskill = objects[obj_type].oc_skill;
890 hits = (oskill != -P_BOW && oskill != -P_CROSSBOW
891 && oskill != -P_DART && oskill != -P_SHURIKEN
892 && oskill != P_SPEAR
893 && oskill != P_KNIFE); /* but not dagger */
894 break;
896 case ARMOR_CLASS:
897 hits = (objects[obj_type].oc_armcat != ARM_GLOVES);
898 break;
899 case TOOL_CLASS:
900 hits = (obj_type != SKELETON_KEY && obj_type != LOCK_PICK
901 && obj_type != CREDIT_CARD && obj_type != TALLOW_CANDLE
902 && obj_type != WAX_CANDLE && obj_type != LENSES
903 && obj_type != TIN_WHISTLE && obj_type != MAGIC_WHISTLE);
904 break;
905 case ROCK_CLASS: /* includes boulder */
906 if (obj_type != STATUE || mons[otmp->corpsenm].msize > MZ_TINY)
907 hits = TRUE;
908 break;
909 case FOOD_CLASS:
910 if (obj_type == CORPSE && mons[otmp->corpsenm].msize > MZ_TINY)
911 hits = TRUE;
912 else
913 hits = (obj_type == MEAT_STICK
914 || obj_type == HUGE_CHUNK_OF_MEAT);
915 break;
916 case SPBOOK_CLASS:
917 case WAND_CLASS:
918 case BALL_CLASS:
919 case CHAIN_CLASS:
920 hits = TRUE;
921 break;
922 default:
923 break;
926 if (hits && whodidit != -1) {
927 hit_bars(obj_p, x,y, barsx,barsy, whodidit, FALSE);
930 return hits;
933 /*mthrowu.c*/