dumplog message history groundwork
[aNetHack.git] / src / mthrowu.c
blobc36c0fe75a27dd352f7389ecf11d74d631088e48
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(monmulti, (struct monst *, struct obj *, struct obj *));
8 STATIC_DCL void FDECL(monshoot, (struct monst *, struct obj *, struct obj *));
9 STATIC_DCL int FDECL(drop_throw, (struct obj *, BOOLEAN_P, int, int));
10 STATIC_DCL boolean FDECL(m_lined_up, (struct monst *, struct monst *));
12 #define URETREATING(x, y) \
13 (distmin(u.ux, u.uy, x, y) > distmin(u.ux0, u.uy0, x, y))
15 #define POLE_LIM 5 /* How far monsters can use pole-weapons */
17 #define PET_MISSILE_RANGE2 36 /* Square of distance within which pets shoot */
20 * Keep consistent with breath weapons in zap.c, and AD_* in monattk.h.
22 STATIC_OVL NEARDATA const char *breathwep[] = {
23 "fragments", "fire", "frost", "sleep gas", "a disintegration blast",
24 "lightning", "poison gas", "acid", "strange breath #8",
25 "strange breath #9"
28 extern boolean notonhead; /* for long worms */
30 /* hero is hit by something other than a monster */
31 int
32 thitu(tlev, dam, obj, name)
33 int tlev, dam;
34 struct obj *obj;
35 const char *name; /* if null, then format `obj' */
37 const char *onm, *knm;
38 boolean is_acid;
39 int kprefix = KILLED_BY_AN;
40 char onmbuf[BUFSZ], knmbuf[BUFSZ];
42 if (!name) {
43 if (!obj)
44 panic("thitu: name & obj both null?");
45 name =
46 strcpy(onmbuf, (obj->quan > 1L) ? doname(obj) : mshot_xname(obj));
47 knm = strcpy(knmbuf, killer_xname(obj));
48 kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */
49 } else {
50 knm = name;
51 /* [perhaps ought to check for plural here to] */
52 if (!strncmpi(name, "the ", 4) || !strncmpi(name, "an ", 3)
53 || !strncmpi(name, "a ", 2))
54 kprefix = KILLED_BY;
56 onm = (obj && obj_is_pname(obj)) ? the(name)
57 : (obj && obj->quan > 1L) ? name
58 : an(name);
59 is_acid = (obj && obj->otyp == ACID_VENOM);
61 if (u.uac + tlev <= rnd(20)) {
62 if (Blind || !flags.verbose)
63 pline("It misses.");
64 else
65 You("are almost hit by %s.", onm);
66 return 0;
67 } else {
68 if (Blind || !flags.verbose)
69 You("are hit%s", exclam(dam));
70 else
71 You("are hit by %s%s", onm, exclam(dam));
73 if (obj && objects[obj->otyp].oc_material == SILVER && Hate_silver) {
74 /* extra damage already applied by dmgval() */
75 pline_The("silver sears your flesh!");
76 exercise(A_CON, FALSE);
78 if (is_acid && Acid_resistance) {
79 pline("It doesn't seem to hurt you.");
80 } else {
81 if (is_acid)
82 pline("It burns!");
83 losehp(dam, knm, kprefix); /* acid damage */
84 exercise(A_STR, FALSE);
86 return 1;
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).
94 STATIC_OVL int
95 drop_throw(obj, ohit, x, y)
96 register struct obj *obj;
97 boolean ohit;
98 int x, y;
100 int retvalu = 1;
101 int create;
102 struct monst *mtmp;
103 struct trap *t;
105 if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS
106 || (ohit && obj->otyp == EGG))
107 create = 0;
108 else if (ohit && (is_multigen(obj) || obj->otyp == ROCK))
109 create = !rn2(3);
110 else
111 create = 1;
113 if (create
114 && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) && (t = t_at(x, y))
115 && ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT)))) {
116 int objgone = 0;
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")) {
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;
131 } else
132 obfree(obj, (struct obj *) 0);
133 return retvalu;
136 /* The monster that's being shot at when one monster shoots at another */
137 STATIC_OVL struct monst *target = 0;
138 /* The monster that's doing the shooting/throwing */
139 STATIC_OVL struct monst *archer = 0;
141 /* calculate multishot volley count for mtmp throwing otmp (if not ammo) or
142 shooting otmp with mwep (if otmp is ammo and mwep appropriate launcher) */
143 STATIC_OVL int
144 monmulti(mtmp, otmp, mwep)
145 struct monst *mtmp;
146 struct obj *otmp, *mwep;
148 int skill = (int) objects[otmp->otyp].oc_skill;
149 int multishot = 1;
151 if (otmp->quan > 1L /* no point checking if there's only 1 */
152 /* ammo requires corresponding launcher be wielded */
153 && (is_ammo(otmp)
154 ? matching_launcher(otmp, mwep)
155 /* otherwise any stackable (non-ammo) weapon */
156 : otmp->oclass == WEAPON_CLASS)
157 && !mtmp->mconf) {
158 /* Assumes lords are skilled, princes are expert */
159 if (is_prince(mtmp->data))
160 multishot += 2;
161 else if (is_lord(mtmp->data))
162 multishot++;
163 /* fake players treated as skilled (regardless of role limits) */
164 else if (is_mplayer(mtmp->data))
165 multishot++;
167 /* this portion is different from hero multishot; from slash'em?
169 /* Elven Craftsmanship makes for light, quick bows */
170 if (otmp->otyp == ELVEN_ARROW && !otmp->cursed)
171 multishot++;
172 if (ammo_and_launcher(otmp, uwep) && mwep->otyp == ELVEN_BOW
173 && !mwep->cursed)
174 multishot++;
175 /* 1/3 of launcher enchantment */
176 if (ammo_and_launcher(otmp, mwep) && mwep->spe > 1)
177 multishot += (long) rounddiv(mwep->spe, 3);
178 /* Some randomness */
179 multishot = (long) rnd((int) multishot);
181 /* class bonus */
182 switch (monsndx(mtmp->data)) {
183 case PM_CAVEMAN: /* give bonus for low-tech gear */
184 if (skill == -P_SLING || skill == P_SPEAR)
185 multishot++;
186 break;
187 case PM_MONK: /* allow higher volley count */
188 if (skill == -P_SHURIKEN)
189 multishot++;
190 break;
191 case PM_RANGER:
192 if (skill != P_DAGGER)
193 multishot++;
194 break;
195 case PM_ROGUE:
196 if (skill == P_DAGGER)
197 multishot++;
198 break;
199 case PM_NINJA:
200 if (skill == -P_SHURIKEN || skill == -P_DART)
201 multishot++;
202 /*FALLTHRU*/
203 case PM_SAMURAI:
204 if (otmp->otyp == YA && mwep->otyp == YUMI)
205 multishot++;
206 break;
207 default:
208 break;
210 /* racial bonus */
211 if ((is_elf(mtmp->data) && otmp->otyp == ELVEN_ARROW
212 && mwep->otyp == ELVEN_BOW)
213 || (is_orc(mtmp->data) && otmp->otyp == ORCISH_ARROW
214 && mwep->otyp == ORCISH_BOW)
215 || (is_gnome(mtmp->data) && otmp->otyp == CROSSBOW_BOLT
216 && mwep->otyp == CROSSBOW))
217 multishot++;
220 if (otmp->quan < multishot)
221 multishot = (int) otmp->quan;
222 if (multishot < 1)
223 multishot = 1;
224 return multishot;
227 /* mtmp throws otmp, or shoots otmp with mwep, at hero or at monster mtarg */
228 STATIC_OVL void
229 monshoot(mtmp, otmp, mwep)
230 struct monst *mtmp;
231 struct obj *otmp, *mwep;
233 struct monst *mtarg = target;
234 int dm = distmin(mtmp->mx, mtmp->my,
235 mtarg ? mtarg->mx : mtmp->mux,
236 mtarg ? mtarg->my : mtmp->muy),
237 multishot = monmulti(mtmp, otmp, mwep);
239 * Caller must have called linedup() to set up tbx, tby.
242 if (canseemon(mtmp)) {
243 const char *onm;
244 char onmbuf[BUFSZ], trgbuf[BUFSZ];
246 if (multishot > 1) {
247 /* "N arrows"; multishot > 1 implies otmp->quan > 1, so
248 xname()'s result will already be pluralized */
249 Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
250 onm = onmbuf;
251 } else {
252 /* "an arrow" */
253 onm = singular(otmp, xname);
254 onm = obj_is_pname(otmp) ? the(onm) : an(onm);
256 m_shot.s = ammo_and_launcher(otmp, mwep) ? TRUE : FALSE;
257 Strcpy(trgbuf, mtarg ? mon_nam(mtarg) : "");
258 if (!strcmp(trgbuf, "it"))
259 Strcpy(trgbuf, humanoid(mtmp->data) ? "someone" : something);
260 pline("%s %s %s%s%s!", Monnam(mtmp),
261 m_shot.s ? "shoots" : "throws", onm,
262 mtarg ? " at " : "", trgbuf);
263 m_shot.o = otmp->otyp;
264 } else {
265 m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */
267 m_shot.n = multishot;
268 for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) {
269 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), dm, otmp);
270 /* conceptually all N missiles are in flight at once, but
271 if mtmp gets killed (shot kills adjacent gas spore and
272 triggers explosion, perhaps), inventory will be dropped
273 and otmp might go away via merging into another stack */
274 if (mtmp->mhp <= 0 && m_shot.i < m_shot.n)
275 /* cancel pending shots (perhaps ought to give a message here
276 since we gave one above about throwing/shooting N missiles) */
277 break; /* endmultishot(FALSE); */
279 /* reset 'm_shot' */
280 m_shot.n = m_shot.i = 0;
281 m_shot.o = STRANGE_OBJECT;
282 m_shot.s = FALSE;
285 /* an object launched by someone/thing other than player attacks a monster;
286 return 1 if the object has stopped moving (hit or its range used up) */
288 ohitmon(mtmp, otmp, range, verbose)
289 struct monst *mtmp; /* accidental target, located at <bhitpos.x,.y> */
290 struct obj *otmp; /* missile; might be destroyed by drop_throw */
291 int range; /* how much farther will object travel if it misses;
292 use -1 to signify to keep going even after hit,
293 unless it's gone (used for rolling_boulder_traps) */
294 boolean verbose; /* give message(s) even when you can't see what happened */
296 int damage, tmp;
297 boolean vis, ismimic;
298 int objgone = 1;
299 struct obj *mon_launcher = archer ? MON_WEP(archer) : NULL;
301 notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
302 ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER;
303 vis = cansee(bhitpos.x, bhitpos.y);
305 tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE);
306 /* High level monsters will be more likely to hit */
307 /* This check applies only if this monster is the target
308 * the archer was aiming at. */
309 if (archer && target == mtmp) {
310 if (archer->m_lev > 5)
311 tmp += archer->m_lev - 5;
312 if (mon_launcher && mon_launcher->oartifact)
313 tmp += spec_abon(mon_launcher, mtmp);
315 if (tmp < rnd(20)) {
316 if (!ismimic) {
317 if (vis)
318 miss(distant_name(otmp, mshot_xname), mtmp);
319 else if (verbose && !target)
320 pline("It is missed.");
322 if (!range) { /* Last position; object drops */
323 (void) drop_throw(otmp, 0, mtmp->mx, mtmp->my);
324 return 1;
326 } else if (otmp->oclass == POTION_CLASS) {
327 if (ismimic)
328 seemimic(mtmp);
329 mtmp->msleeping = 0;
330 if (vis)
331 otmp->dknown = 1;
332 potionhit(mtmp, otmp, FALSE);
333 return 1;
334 } else {
335 damage = dmgval(otmp, mtmp);
336 if (otmp->otyp == ACID_VENOM && resists_acid(mtmp))
337 damage = 0;
338 if (ismimic)
339 seemimic(mtmp);
340 mtmp->msleeping = 0;
341 if (vis) {
342 if (otmp->otyp == EGG)
343 pline("Splat! %s is hit with %s egg!", Monnam(mtmp),
344 otmp->known ? an(mons[otmp->corpsenm].mname) : "an");
345 else
346 hit(distant_name(otmp, mshot_xname), mtmp, exclam(damage));
347 } else if (verbose && !target)
348 pline("%s%s is hit%s", (otmp->otyp == EGG) ? "Splat! " : "",
349 Monnam(mtmp), exclam(damage));
351 if (otmp->opoisoned && is_poisonable(otmp)) {
352 if (resists_poison(mtmp)) {
353 if (vis)
354 pline_The("poison doesn't seem to affect %s.",
355 mon_nam(mtmp));
356 } else {
357 if (rn2(30)) {
358 damage += rnd(6);
359 } else {
360 if (vis)
361 pline_The("poison was deadly...");
362 damage = mtmp->mhp;
366 if (objects[otmp->otyp].oc_material == SILVER
367 && mon_hates_silver(mtmp)) {
368 if (vis)
369 pline_The("silver sears %s flesh!", s_suffix(mon_nam(mtmp)));
370 else if (verbose && !target)
371 pline("Its flesh is seared!");
373 if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx, mtmp->my)) {
374 if (resists_acid(mtmp)) {
375 if (vis || (verbose && !target))
376 pline("%s is unaffected.", Monnam(mtmp));
377 } else {
378 if (vis)
379 pline_The("%s burns %s!", hliquid("acid"), mon_nam(mtmp));
380 else if (verbose && !target)
381 pline("It is burned!");
384 if (otmp->otyp == EGG && touch_petrifies(&mons[otmp->corpsenm])) {
385 if (!munstone(mtmp, TRUE))
386 minstapetrify(mtmp, TRUE);
387 if (resists_ston(mtmp))
388 damage = 0;
391 if (mtmp->mhp > 0) { /* might already be dead (if petrified) */
392 mtmp->mhp -= damage;
393 if (mtmp->mhp < 1) {
394 if (vis || (verbose && !target))
395 pline("%s is %s!", Monnam(mtmp),
396 (nonliving(mtmp->data) || is_vampshifter(mtmp)
397 || !canspotmon(mtmp)) ? "destroyed" : "killed");
398 /* don't blame hero for unknown rolling boulder trap */
399 if (!context.mon_moving && (otmp->otyp != BOULDER
400 || range >= 0 || otmp->otrapped))
401 xkilled(mtmp, XKILL_NOMSG);
402 else
403 mondied(mtmp);
407 /* blinding venom and cream pie do 0 damage, but verify
408 that the target is still alive anyway */
409 if (mtmp->mhp > 0
410 && can_blnd((struct monst *) 0, mtmp,
411 (uchar) ((otmp->otyp == BLINDING_VENOM) ? AT_SPIT
412 : AT_WEAP),
413 otmp)) {
414 if (vis && mtmp->mcansee)
415 pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp)));
416 mtmp->mcansee = 0;
417 tmp = (int) mtmp->mblinded + rnd(25) + 20;
418 if (tmp > 127)
419 tmp = 127;
420 mtmp->mblinded = tmp;
423 objgone = drop_throw(otmp, 1, bhitpos.x, bhitpos.y);
424 if (!objgone && range == -1) { /* special case */
425 obj_extract_self(otmp); /* free it for motion again */
426 return 0;
428 return 1;
430 return 0;
433 #define MT_FLIGHTCHECK(pre) \
434 (/* missile hits edge of screen */ \
435 !isok(bhitpos.x + dx, bhitpos.y + dy) \
436 /* missile hits the wall */ \
437 || IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ) \
438 /* missile hit closed door */ \
439 || closed_door(bhitpos.x + dx, bhitpos.y + dy) \
440 /* missile might hit iron bars */ \
441 /* the random chance for small objects hitting bars is */ \
442 /* skipped when reaching them at point blank range */ \
443 || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS \
444 && hits_bars(&singleobj, \
445 bhitpos.x, bhitpos.y, \
446 bhitpos.x + dx, bhitpos.y + dy, \
447 ((pre) ? 0 : !rn2(5)), 0)) \
448 /* Thrown objects "sink" */ \
449 || (!(pre) && IS_SINK(levl[bhitpos.x][bhitpos.y].typ)))
451 void
452 m_throw(mon, x, y, dx, dy, range, obj)
453 struct monst *mon; /* launching monster */
454 int x, y, dx, dy, range; /* launch point, direction, and range */
455 struct obj *obj; /* missile (or stack providing it) */
457 struct monst *mtmp;
458 struct obj *singleobj;
459 char sym = obj->oclass;
460 int hitu = 0, oldumort, blindinc = 0;
462 bhitpos.x = x;
463 bhitpos.y = y;
464 notonhead = FALSE; /* reset potentially stale value */
466 if (obj->quan == 1L) {
468 * Remove object from minvent. This cannot be done later on;
469 * what if the player dies before then, leaving the monster
470 * with 0 daggers? (This caused the infamous 2^32-1 orcish
471 * dagger bug).
473 * VENOM is not in minvent - it should already be OBJ_FREE.
474 * The extract below does nothing.
477 /* not possibly_unwield, which checks the object's */
478 /* location, not its existence */
479 if (MON_WEP(mon) == obj)
480 setmnotwielded(mon, obj);
481 obj_extract_self(obj);
482 singleobj = obj;
483 obj = (struct obj *) 0;
484 } else {
485 singleobj = splitobj(obj, 1L);
486 obj_extract_self(singleobj);
489 singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */
491 if ((singleobj->cursed || singleobj->greased) && (dx || dy) && !rn2(7)) {
492 if (canseemon(mon) && flags.verbose) {
493 if (is_ammo(singleobj))
494 pline("%s misfires!", Monnam(mon));
495 else
496 pline("%s as %s throws it!", Tobjnam(singleobj, "slip"),
497 mon_nam(mon));
499 dx = rn2(3) - 1;
500 dy = rn2(3) - 1;
501 /* check validity of new direction */
502 if (!dx && !dy) {
503 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
504 return;
508 if (MT_FLIGHTCHECK(TRUE)) {
509 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
510 return;
513 /* Note: drop_throw may destroy singleobj. Since obj must be destroyed
514 * early to avoid the dagger bug, anyone who modifies this code should
515 * be careful not to use either one after it's been freed.
517 if (sym)
518 tmp_at(DISP_FLASH, obj_to_glyph(singleobj));
519 while (range-- > 0) { /* Actually the loop is always exited by break */
520 bhitpos.x += dx;
521 bhitpos.y += dy;
522 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
523 if (ohitmon(mtmp, singleobj, range, TRUE))
524 break;
525 } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
526 if (multi)
527 nomul(0);
529 if (singleobj->oclass == GEM_CLASS
530 && singleobj->otyp <= LAST_GEM + 9 /* 9 glass colors */
531 && is_unicorn(youmonst.data)) {
532 if (singleobj->otyp > LAST_GEM) {
533 You("catch the %s.", xname(singleobj));
534 You("are not interested in %s junk.",
535 s_suffix(mon_nam(mon)));
536 makeknown(singleobj->otyp);
537 dropy(singleobj);
538 } else {
539 You(
540 "accept %s gift in the spirit in which it was intended.",
541 s_suffix(mon_nam(mon)));
542 (void) hold_another_object(singleobj,
543 "You catch, but drop, %s.",
544 xname(singleobj),
545 "You catch:");
547 break;
549 if (singleobj->oclass == POTION_CLASS) {
550 if (!Blind)
551 singleobj->dknown = 1;
552 potionhit(&youmonst, singleobj, FALSE);
553 break;
555 oldumort = u.umortality;
556 switch (singleobj->otyp) {
557 int dam, hitv;
558 case EGG:
559 if (!touch_petrifies(&mons[singleobj->corpsenm])) {
560 impossible("monster throwing egg type %d",
561 singleobj->corpsenm);
562 hitu = 0;
563 break;
565 /* fall through */
566 case CREAM_PIE:
567 case BLINDING_VENOM:
568 hitu = thitu(8, 0, singleobj, (char *) 0);
569 break;
570 default:
571 dam = dmgval(singleobj, &youmonst);
572 hitv = 3 - distmin(u.ux, u.uy, mon->mx, mon->my);
573 if (hitv < -4)
574 hitv = -4;
575 if (is_elf(mon->data)
576 && objects[singleobj->otyp].oc_skill == P_BOW) {
577 hitv++;
578 if (MON_WEP(mon) && MON_WEP(mon)->otyp == ELVEN_BOW)
579 hitv++;
580 if (singleobj->otyp == ELVEN_ARROW)
581 dam++;
583 if (bigmonst(youmonst.data))
584 hitv++;
585 hitv += 8 + singleobj->spe;
586 if (dam < 1)
587 dam = 1;
588 hitu = thitu(hitv, dam, singleobj, (char *) 0);
590 if (hitu && singleobj->opoisoned && is_poisonable(singleobj)) {
591 char onmbuf[BUFSZ], knmbuf[BUFSZ];
593 Strcpy(onmbuf, xname(singleobj));
594 Strcpy(knmbuf, killer_xname(singleobj));
595 poisoned(onmbuf, A_STR, knmbuf,
596 /* if damage triggered life-saving,
597 poison is limited to attrib loss */
598 (u.umortality > oldumort) ? 0 : 10, TRUE);
600 if (hitu && can_blnd((struct monst *) 0, &youmonst,
601 (uchar) ((singleobj->otyp == BLINDING_VENOM)
602 ? AT_SPIT
603 : AT_WEAP),
604 singleobj)) {
605 blindinc = rnd(25);
606 if (singleobj->otyp == CREAM_PIE) {
607 if (!Blind)
608 pline("Yecch! You've been creamed.");
609 else
610 pline("There's %s sticky all over your %s.",
611 something, body_part(FACE));
612 } else if (singleobj->otyp == BLINDING_VENOM) {
613 const char *eyes = body_part(EYE);
615 if (eyecount(youmonst.data) != 1)
616 eyes = makeplural(eyes);
617 /* venom in the eyes */
618 if (!Blind)
619 pline_The("venom blinds you.");
620 else
621 Your("%s %s.", eyes, vtense(eyes, "sting"));
624 if (hitu && singleobj->otyp == EGG) {
625 if (!Stoned && !Stone_resistance
626 && !(poly_when_stoned(youmonst.data)
627 && polymon(PM_STONE_GOLEM))) {
628 make_stoned(5L, (char *) 0, KILLED_BY, "");
631 stop_occupation();
632 if (hitu) {
633 (void) drop_throw(singleobj, hitu, u.ux, u.uy);
634 break;
637 if (!range /* reached end of path */
638 || MT_FLIGHTCHECK(FALSE)) {
639 if (singleobj) { /* hits_bars might have destroyed it */
640 if (m_shot.n > 1 && (cansee(bhitpos.x, bhitpos.y)
641 || (archer && canseemon(archer))))
642 pline("%s misses.", The(mshot_xname(singleobj)));
643 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
645 break;
647 tmp_at(bhitpos.x, bhitpos.y);
648 delay_output();
650 tmp_at(bhitpos.x, bhitpos.y);
651 delay_output();
652 tmp_at(DISP_END, 0);
654 if (blindinc) {
655 u.ucreamed += blindinc;
656 make_blinded(Blinded + (long) blindinc, FALSE);
657 if (!Blind)
658 Your1(vision_clears);
662 #undef MT_FLIGHTCHECK
664 /* Monster throws item at another monster */
666 thrwmm(mtmp, mtarg)
667 struct monst *mtmp, *mtarg;
669 struct obj *otmp, *mwep;
670 register xchar x, y;
671 boolean ispole;
673 /* Polearms won't be applied by monsters against other monsters */
674 if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
675 mtmp->weapon_check = NEED_RANGED_WEAPON;
676 /* mon_wield_item resets weapon_check as appropriate */
677 if (mon_wield_item(mtmp) != 0)
678 return 0;
681 /* Pick a weapon */
682 otmp = select_rwep(mtmp);
683 if (!otmp)
684 return 0;
685 ispole = is_pole(otmp);
687 x = mtmp->mx;
688 y = mtmp->my;
690 mwep = MON_WEP(mtmp); /* wielded weapon */
692 if (!ispole && m_lined_up(mtarg, mtmp)) {
693 int chance = max(BOLT_LIM - distmin(x, y, mtarg->mx, mtarg->my), 1);
695 if (!mtarg->mflee || !rn2(chance)) {
696 if (ammo_and_launcher(otmp, mwep)
697 && dist2(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my)
698 > PET_MISSILE_RANGE2)
699 return 0; /* Out of range */
700 /* Set target monster */
701 target = mtarg;
702 archer = mtmp;
703 monshoot(mtmp, otmp, mwep); /* multishot shooting or throwing */
704 archer = target = (struct monst *) 0;
705 nomul(0);
706 return 1;
709 return 0;
712 /* monster spits substance at monster */
714 spitmm(mtmp, mattk, mtarg)
715 struct monst *mtmp, *mtarg;
716 struct attack *mattk;
718 struct obj *otmp;
720 if (mtmp->mcan) {
721 if (!Deaf)
722 pline("A dry rattle comes from %s throat.",
723 s_suffix(mon_nam(mtmp)));
724 return 0;
726 if (m_lined_up(mtarg, mtmp)) {
727 switch (mattk->adtyp) {
728 case AD_BLND:
729 case AD_DRST:
730 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
731 break;
732 default:
733 impossible("bad attack type in spitmu");
734 /* fall through */
735 case AD_ACID:
736 otmp = mksobj(ACID_VENOM, TRUE, FALSE);
737 break;
739 if (!rn2(BOLT_LIM-distmin(mtmp->mx,mtmp->my,mtarg->mx,mtarg->my))) {
740 if (canseemon(mtmp))
741 pline("%s spits venom!", Monnam(mtmp));
742 target = mtarg;
743 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
744 distmin(mtmp->mx,mtmp->my,mtarg->mx,mtarg->my), otmp);
745 target = (struct monst *)0;
746 nomul(0);
748 /* If this is a pet, it'll get hungry. Minions and
749 * spell beings won't hunger */
750 if (mtmp->mtame && !mtmp->isminion) {
751 struct edog *dog = EDOG(mtmp);
753 /* Hunger effects will catch up next move */
754 if (dog->hungrytime > 1)
755 dog->hungrytime -= 5;
758 return 1;
761 return 0;
764 /* monster breathes at monster (ranged) */
766 breamm(mtmp, mattk, mtarg)
767 struct monst *mtmp, *mtarg;
768 struct attack *mattk;
770 /* if new breath types are added, change AD_ACID to max type */
771 int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp ;
773 if (m_lined_up(mtarg, mtmp)) {
774 if (mtmp->mcan) {
775 if (!Deaf) {
776 if (canseemon(mtmp))
777 pline("%s coughs.", Monnam(mtmp));
778 else
779 You_hear("a cough.");
781 return 0;
783 if (!mtmp->mspec_used && rn2(3)) {
784 if ((typ >= AD_MAGM) && (typ <= AD_ACID)) {
785 if (canseemon(mtmp))
786 pline("%s breathes %s!", Monnam(mtmp), breathwep[typ - 1]);
787 dobuzz((int) (-20 - (typ - 1)), (int)mattk->damn,
788 mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), FALSE);
789 nomul(0);
790 /* breath runs out sometimes. Also, give monster some
791 * cunning; don't breath if the target fell asleep.
793 mtmp->mspec_used = 6 + rn2(18);
795 /* If this is a pet, it'll get hungry. Minions and
796 * spell beings won't hunger */
797 if (mtmp->mtame && !mtmp->isminion) {
798 struct edog *dog = EDOG(mtmp);
800 /* Hunger effects will catch up next move */
801 if (dog->hungrytime >= 10)
802 dog->hungrytime -= 10;
804 } else impossible("Breath weapon %d used", typ-1);
805 } else
806 return 0;
808 return 1;
813 /* remove an entire item from a monster's inventory; destroy that item */
814 void
815 m_useupall(mon, obj)
816 struct monst *mon;
817 struct obj *obj;
819 obj_extract_self(obj);
820 if (obj->owornmask) {
821 if (obj == MON_WEP(mon))
822 mwepgone(mon);
823 mon->misc_worn_check &= ~obj->owornmask;
824 update_mon_intrinsics(mon, obj, FALSE, FALSE);
825 obj->owornmask = 0L;
827 obfree(obj, (struct obj *) 0);
830 /* remove one instance of an item from a monster's inventory */
831 void
832 m_useup(mon, obj)
833 struct monst *mon;
834 struct obj *obj;
836 if (obj->quan > 1L) {
837 obj->quan--;
838 obj->owt = weight(obj);
839 } else {
840 m_useupall(mon, obj);
844 /* monster attempts ranged weapon attack against player */
845 void
846 thrwmu(mtmp)
847 struct monst *mtmp;
849 struct obj *otmp, *mwep;
850 xchar x, y;
851 const char *onm;
853 /* Rearranged beginning so monsters can use polearms not in a line */
854 if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
855 mtmp->weapon_check = NEED_RANGED_WEAPON;
856 /* mon_wield_item resets weapon_check as appropriate */
857 if (mon_wield_item(mtmp) != 0)
858 return;
861 /* Pick a weapon */
862 otmp = select_rwep(mtmp);
863 if (!otmp)
864 return;
866 if (is_pole(otmp)) {
867 int dam, hitv;
869 if (otmp != MON_WEP(mtmp))
870 return; /* polearm must be wielded */
871 if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) > POLE_LIM
872 || !couldsee(mtmp->mx, mtmp->my))
873 return; /* Out of range, or intervening wall */
875 if (canseemon(mtmp)) {
876 onm = xname(otmp);
877 pline("%s thrusts %s.", Monnam(mtmp),
878 obj_is_pname(otmp) ? the(onm) : an(onm));
881 dam = dmgval(otmp, &youmonst);
882 hitv = 3 - distmin(u.ux, u.uy, mtmp->mx, mtmp->my);
883 if (hitv < -4)
884 hitv = -4;
885 if (bigmonst(youmonst.data))
886 hitv++;
887 hitv += 8 + otmp->spe;
888 if (dam < 1)
889 dam = 1;
891 (void) thitu(hitv, dam, otmp, (char *) 0);
892 stop_occupation();
893 return;
896 x = mtmp->mx;
897 y = mtmp->my;
898 /* If you are coming toward the monster, the monster
899 * should try to soften you up with missiles. If you are
900 * going away, you are probably hurt or running. Give
901 * chase, but if you are getting too far away, throw.
903 if (!lined_up(mtmp)
904 || (URETREATING(x, y)
905 && rn2(BOLT_LIM - distmin(x, y, mtmp->mux, mtmp->muy))))
906 return;
908 mwep = MON_WEP(mtmp); /* wielded weapon */
909 monshoot(mtmp, otmp, mwep); /* multishot shooting or throwing */
910 nomul(0);
913 /* monster spits substance at you */
915 spitmu(mtmp, mattk)
916 struct monst *mtmp;
917 struct attack *mattk;
919 struct obj *otmp;
921 if (mtmp->mcan) {
922 if (!Deaf)
923 pline("A dry rattle comes from %s throat.",
924 s_suffix(mon_nam(mtmp)));
925 return 0;
927 if (lined_up(mtmp)) {
928 switch (mattk->adtyp) {
929 case AD_BLND:
930 case AD_DRST:
931 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
932 break;
933 default:
934 impossible("bad attack type in spitmu");
935 /* fall through */
936 case AD_ACID:
937 otmp = mksobj(ACID_VENOM, TRUE, FALSE);
938 break;
940 if (!rn2(BOLT_LIM
941 - distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy))) {
942 if (canseemon(mtmp))
943 pline("%s spits venom!", Monnam(mtmp));
944 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
945 distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
946 nomul(0);
947 return 0;
948 } else {
949 obj_extract_self(otmp);
950 obfree(otmp, (struct obj *) 0);
953 return 0;
956 /* monster breathes at you (ranged) */
958 breamu(mtmp, mattk)
959 struct monst *mtmp;
960 struct attack *mattk;
962 /* if new breath types are added, change AD_ACID to max type */
963 int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp;
965 if (lined_up(mtmp)) {
966 if (mtmp->mcan) {
967 if (!Deaf) {
968 if (canseemon(mtmp))
969 pline("%s coughs.", Monnam(mtmp));
970 else
971 You_hear("a cough.");
973 return 0;
975 if (!mtmp->mspec_used && rn2(3)) {
976 if ((typ >= AD_MAGM) && (typ <= AD_ACID)) {
977 if (canseemon(mtmp))
978 pline("%s breathes %s!", Monnam(mtmp),
979 breathwep[typ - 1]);
980 buzz((int) (-20 - (typ - 1)), (int) mattk->damn, mtmp->mx,
981 mtmp->my, sgn(tbx), sgn(tby));
982 nomul(0);
983 /* breath runs out sometimes. Also, give monster some
984 * cunning; don't breath if the player fell asleep.
986 if (!rn2(3))
987 mtmp->mspec_used = 10 + rn2(20);
988 if (typ == AD_SLEE && !Sleep_resistance)
989 mtmp->mspec_used += rnd(20);
990 } else
991 impossible("Breath weapon %d used", typ - 1);
994 return 1;
997 boolean
998 linedup(ax, ay, bx, by, boulderhandling)
999 register xchar ax, ay, bx, by;
1000 int boulderhandling; /* 0=block, 1=ignore, 2=conditionally block */
1002 int dx, dy, boulderspots;
1004 /* These two values are set for use after successful return. */
1005 tbx = ax - bx;
1006 tby = ay - by;
1008 /* sometimes displacement makes a monster think that you're at its
1009 own location; prevent it from throwing and zapping in that case */
1010 if (!tbx && !tby)
1011 return FALSE;
1013 if ((!tbx || !tby || abs(tbx) == abs(tby)) /* straight line or diagonal */
1014 && distmin(tbx, tby, 0, 0) < BOLT_LIM) {
1015 if ((ax == u.ux && ay == u.uy) ? (boolean) couldsee(bx, by)
1016 : clear_path(ax, ay, bx, by))
1017 return TRUE;
1018 /* don't have line of sight, but might still be lined up
1019 if that lack of sight is due solely to boulders */
1020 if (boulderhandling == 0)
1021 return FALSE;
1022 dx = sgn(ax - bx), dy = sgn(ay - by);
1023 boulderspots = 0;
1024 do {
1025 /* <bx,by> is guaranteed to eventually converge with <ax,ay> */
1026 bx += dx, by += dy;
1027 if (IS_ROCK(levl[bx][by].typ) || closed_door(bx, by))
1028 return FALSE;
1029 if (sobj_at(BOULDER, bx, by))
1030 ++boulderspots;
1031 } while (bx != ax || by != ay);
1032 /* reached target position without encountering obstacle */
1033 if (boulderhandling == 1 || rn2(2 + boulderspots) < 2)
1034 return TRUE;
1036 return FALSE;
1039 STATIC_OVL boolean
1040 m_lined_up(mtarg, mtmp)
1041 struct monst *mtarg, *mtmp;
1043 return (linedup(mtarg->mx, mtarg->my, mtmp->mx, mtmp->my, 0));
1047 /* is mtmp in position to use ranged attack? */
1048 boolean
1049 lined_up(mtmp)
1050 register struct monst *mtmp;
1052 boolean ignore_boulders;
1054 /* hero concealment usually trumps monst awareness of being lined up */
1055 if (Upolyd && rn2(25)
1056 && (u.uundetected || (youmonst.m_ap_type != M_AP_NOTHING
1057 && youmonst.m_ap_type != M_AP_MONSTER)))
1058 return FALSE;
1060 ignore_boulders = (throws_rocks(mtmp->data)
1061 || m_carrying(mtmp, WAN_STRIKING));
1062 return linedup(mtmp->mux, mtmp->muy, mtmp->mx, mtmp->my,
1063 ignore_boulders ? 1 : 2);
1066 /* check if a monster is carrying a particular item */
1067 struct obj *
1068 m_carrying(mtmp, type)
1069 struct monst *mtmp;
1070 int type;
1072 register struct obj *otmp;
1074 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
1075 if (otmp->otyp == type)
1076 return otmp;
1077 return (struct obj *) 0;
1080 void
1081 hit_bars(objp, objx, objy, barsx, barsy, your_fault, from_invent)
1082 struct obj **objp; /* *objp will be set to NULL if object breaks */
1083 int objx, objy, barsx, barsy;
1084 boolean your_fault, from_invent;
1086 struct obj *otmp = *objp;
1087 int obj_type = otmp->otyp;
1088 boolean unbreakable = (levl[barsx][barsy].wall_info & W_NONDIGGABLE) != 0;
1090 if (your_fault
1091 ? hero_breaks(otmp, objx, objy, from_invent)
1092 : breaks(otmp, objx, objy)) {
1093 *objp = 0; /* object is now gone */
1094 /* breakage makes its own noises */
1095 if (obj_type == POT_ACID) {
1096 if (cansee(barsx, barsy) && !unbreakable)
1097 pline_The("iron bars are dissolved!");
1098 else
1099 You_hear(Hallucination ? "angry snakes!" : "a hissing noise.");
1100 if (!unbreakable)
1101 dissolve_bars(barsx, barsy);
1104 else if (obj_type == BOULDER || obj_type == HEAVY_IRON_BALL)
1105 pline("Whang!");
1106 else if (otmp->oclass == COIN_CLASS
1107 || objects[obj_type].oc_material == GOLD
1108 || objects[obj_type].oc_material == SILVER)
1109 pline("Clink!");
1110 else
1111 pline("Clonk!");
1114 /* TRUE iff thrown/kicked/rolled object doesn't pass through iron bars */
1115 boolean
1116 hits_bars(obj_p, x, y, barsx, barsy, always_hit, whodidit)
1117 struct obj **obj_p; /* *obj_p will be set to NULL if object breaks */
1118 int x, y, barsx, barsy;
1119 int always_hit; /* caller can force a hit for items which would fit through */
1120 int whodidit; /* 1==hero, 0=other, -1==just check whether it'll pass thru */
1122 struct obj *otmp = *obj_p;
1123 int obj_type = otmp->otyp;
1124 boolean hits = always_hit;
1126 if (!hits)
1127 switch (otmp->oclass) {
1128 case WEAPON_CLASS: {
1129 int oskill = objects[obj_type].oc_skill;
1131 hits = (oskill != -P_BOW && oskill != -P_CROSSBOW
1132 && oskill != -P_DART && oskill != -P_SHURIKEN
1133 && oskill != P_SPEAR
1134 && oskill != P_KNIFE); /* but not dagger */
1135 break;
1137 case ARMOR_CLASS:
1138 hits = (objects[obj_type].oc_armcat != ARM_GLOVES);
1139 break;
1140 case TOOL_CLASS:
1141 hits = (obj_type != SKELETON_KEY && obj_type != LOCK_PICK
1142 && obj_type != CREDIT_CARD && obj_type != TALLOW_CANDLE
1143 && obj_type != WAX_CANDLE && obj_type != LENSES
1144 && obj_type != TIN_WHISTLE && obj_type != MAGIC_WHISTLE);
1145 break;
1146 case ROCK_CLASS: /* includes boulder */
1147 if (obj_type != STATUE || mons[otmp->corpsenm].msize > MZ_TINY)
1148 hits = TRUE;
1149 break;
1150 case FOOD_CLASS:
1151 if (obj_type == CORPSE && mons[otmp->corpsenm].msize > MZ_TINY)
1152 hits = TRUE;
1153 else
1154 hits = (obj_type == MEAT_STICK
1155 || obj_type == HUGE_CHUNK_OF_MEAT);
1156 break;
1157 case SPBOOK_CLASS:
1158 case WAND_CLASS:
1159 case BALL_CLASS:
1160 case CHAIN_CLASS:
1161 hits = TRUE;
1162 break;
1163 default:
1164 break;
1167 if (hits && whodidit != -1) {
1168 hit_bars(obj_p, x,y, barsx,barsy, whodidit, FALSE);
1171 return hits;
1174 /*mthrowu.c*/