1 /* SCCS Id: @(#)mthrowu.c 3.4 2003/05/09 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
7 STATIC_DCL int FDECL(drop_throw,(struct obj *,BOOLEAN_P,int,int));
9 #define URETREATING(x,y) (distmin(u.ux,u.uy,x,y) > distmin(u.ux0,u.uy0,x,y))
11 #define POLE_LIM 5 /* How far monsters can use pole-weapons */
15 STATIC_DCL const char *breathwep[];
20 * Keep consistent with breath weapons in zap.c, and AD_* in monattk.h.
22 STATIC_OVL NEARDATA const char *breathwep[] = {
27 "a disintegration blast",
35 /* hero is hit by something other than a monster */
37 thitu(tlev, dam, obj, name)
40 const char *name; /* if null, then format `obj' */
42 const char *onm, *knm;
44 int kprefix = KILLED_BY_AN;
45 char onmbuf[BUFSZ], knmbuf[BUFSZ];
48 if (!obj) panic("thitu: name & obj both null?");
50 (obj->quan > 1L) ? doname(obj) : mshot_xname(obj));
51 knm = strcpy(knmbuf, killer_xname(obj));
52 kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */
55 /* [perhaps ought to check for plural here to] */
56 if (!strncmpi(name, "the ", 4) ||
57 !strncmpi(name, "an ", 3) ||
58 !strncmpi(name, "a ", 2)) kprefix = KILLED_BY;
60 onm = (obj && obj_is_pname(obj)) ? the(name) :
61 (obj && obj->quan > 1L) ? name : an(name);
62 is_acid = (obj && obj->otyp == ACID_VENOM);
64 if(u.uac + tlev <= rnd(20)) {
65 if(Blind || !flags.verbose) pline("It misses.");
66 else You("are almost hit by %s.", onm);
69 if(Blind || !flags.verbose) You("are hit!");
70 else You("are hit by %s%s", onm, exclam(dam));
72 if (obj && objects[obj->otyp].oc_material == SILVER
73 && hates_silver(youmonst.data)) {
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.");
81 if (is_acid) pline("It burns!");
82 if (Half_physical_damage) dam = (dam+1) / 2;
83 losehp(dam, knm, kprefix);
84 exercise(A_STR, FALSE);
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).
96 drop_throw(obj, ohit, x, y)
97 register struct obj *obj;
106 if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS ||
107 (ohit && obj->otyp == EGG))
109 else if (ohit && (is_multigen(obj) || obj->otyp == ROCK))
113 if (create && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) &&
114 (t = t_at(x, y)) && ((t->ttyp == PIT) ||
115 (t->ttyp == SPIKED_PIT)))) {
118 if (down_gate(x, y) != -1)
119 objgone = ship_object(obj, x, y, FALSE);
121 if (!flooreffects(obj,x,y,"fall")) { /* don't double-dip on damage */
122 place_object(obj, x, y);
123 if (!mtmp && x == u.ux && y == u.uy)
126 passive_obj(mtmp, obj, (struct attack *)0);
131 } else obfree(obj, (struct obj*) 0);
138 /* an object launched by someone/thing other than player attacks a monster;
139 return 1 if the object has stopped moving (hit or its range used up) */
141 ohitmon(mtmp, otmp, range, verbose)
142 struct monst *mtmp; /* accidental target */
143 struct obj *otmp; /* missile; might be destroyed by drop_throw */
144 int range; /* how much farther will object travel if it misses */
145 /* Use -1 to signify to keep going even after hit, */
146 /* unless its gone (used for rolling_boulder_traps) */
147 boolean verbose; /* give message(s) even when you can't see what happened */
150 boolean vis, ismimic;
153 ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER;
154 vis = cansee(bhitpos.x, bhitpos.y);
156 tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE);
159 if (vis) miss(distant_name(otmp, mshot_xname), mtmp);
160 else if (verbose) pline("It is missed.");
162 if (!range) { /* Last position; object drops */
163 (void) drop_throw(otmp, 0, mtmp->mx, mtmp->my);
166 } else if (otmp->oclass == POTION_CLASS) {
167 if (ismimic) seemimic(mtmp);
169 if (vis) otmp->dknown = 1;
170 potionhit(mtmp, otmp, FALSE);
173 damage = dmgval(otmp, mtmp);
174 if (otmp->otyp == ACID_VENOM && resists_acid(mtmp))
176 if (ismimic) seemimic(mtmp);
178 if (vis) hit(distant_name(otmp,mshot_xname), mtmp, exclam(damage));
179 else if (verbose) pline("%s is hit%s", Monnam(mtmp), exclam(damage));
181 if (otmp->opoisoned && is_poisonable(otmp)) {
182 if (resists_poison(mtmp)) {
183 if (vis) pline_The("poison doesn't seem to affect %s.",
189 if (vis) pline_The("poison was deadly...");
194 if (objects[otmp->otyp].oc_material == SILVER &&
195 hates_silver(mtmp->data)) {
196 if (vis) pline_The("silver sears %s flesh!",
197 s_suffix(mon_nam(mtmp)));
198 else if (verbose) pline("Its flesh is seared!");
200 if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx,mtmp->my)) {
201 if (resists_acid(mtmp)) {
203 pline("%s is unaffected.", Monnam(mtmp));
206 if (vis) pline_The("acid burns %s!", mon_nam(mtmp));
207 else if (verbose) pline("It is burned!");
213 pline("%s is %s!", Monnam(mtmp),
214 (nonliving(mtmp->data) || !canspotmon(mtmp))
215 ? "destroyed" : "killed");
216 /* don't blame hero for unknown rolling boulder trap */
217 if (!flags.mon_moving &&
218 (otmp->otyp != BOULDER || range >= 0 || !otmp->otrapped))
223 if (can_blnd((struct monst*)0, mtmp,
224 (uchar)(otmp->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP),
226 if (vis && mtmp->mcansee)
227 pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp)));
229 tmp = (int)mtmp->mblinded + rnd(25) + 20;
230 if (tmp > 127) tmp = 127;
231 mtmp->mblinded = tmp;
234 objgone = drop_throw(otmp, 1, bhitpos.x, bhitpos.y);
235 if (!objgone && range == -1) { /* special case */
236 obj_extract_self(otmp); /* free it for motion again */
245 m_throw(mon, x, y, dx, dy, range, obj)
246 register struct monst *mon;
247 register int x,y,dx,dy,range; /* direction and range */
248 register struct obj *obj;
250 register struct monst *mtmp;
251 struct obj *singleobj;
252 char sym = obj->oclass;
253 int hitu, blindinc = 0;
258 if (obj->quan == 1L) {
260 * Remove object from minvent. This cannot be done later on;
261 * what if the player dies before then, leaving the monster
262 * with 0 daggers? (This caused the infamous 2^32-1 orcish
265 * VENOM is not in minvent - it should already be OBJ_FREE.
266 * The extract below does nothing.
269 /* not possibly_unwield, which checks the object's */
270 /* location, not its existence */
271 if (MON_WEP(mon) == obj) {
272 setmnotwielded(mon,obj);
275 obj_extract_self(obj);
277 obj = (struct obj *) 0;
279 singleobj = splitobj(obj, 1L);
280 obj_extract_self(singleobj);
283 singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */
285 if (singleobj->cursed && (dx || dy) && !rn2(7)) {
286 if(canseemon(mon) && flags.verbose) {
287 if(is_ammo(singleobj))
288 pline("%s misfires!", Monnam(mon));
290 pline("%s as %s throws it!",
291 Tobjnam(singleobj, "slip"), mon_nam(mon));
295 /* check validity of new direction */
297 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
302 /* pre-check for doors, walls and boundaries.
303 Also need to pre-check for bars regardless of direction;
304 the random chance for small objects hitting bars is
305 skipped when reaching them at point blank range */
306 if (!isok(bhitpos.x+dx,bhitpos.y+dy)
307 || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ)
308 || closed_door(bhitpos.x+dx, bhitpos.y+dy)
309 || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS &&
310 hits_bars(&singleobj, bhitpos.x, bhitpos.y, 0, 0))) {
311 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
315 /* Note: drop_throw may destroy singleobj. Since obj must be destroyed
316 * early to avoid the dagger bug, anyone who modifies this code should
317 * be careful not to use either one after it's been freed.
319 if (sym) tmp_at(DISP_FLASH, obj_to_glyph(singleobj));
320 while(range-- > 0) { /* Actually the loop is always exited by break */
323 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
324 if (ohitmon(mtmp, singleobj, range, TRUE))
326 } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
329 if (singleobj->oclass == GEM_CLASS &&
330 singleobj->otyp <= LAST_GEM+9 /* 9 glass colors */
331 && is_unicorn(youmonst.data)) {
332 if (singleobj->otyp > LAST_GEM) {
333 You("catch the %s.", xname(singleobj));
334 You("are not interested in %s junk.",
335 s_suffix(mon_nam(mon)));
336 makeknown(singleobj->otyp);
339 You("accept %s gift in the spirit in which it was intended.",
340 s_suffix(mon_nam(mon)));
341 (void)hold_another_object(singleobj,
342 "You catch, but drop, %s.", xname(singleobj),
347 if (singleobj->oclass == POTION_CLASS) {
348 if (!Blind) singleobj->dknown = 1;
349 potionhit(&youmonst, singleobj, FALSE);
352 switch(singleobj->otyp) {
355 if (!touch_petrifies(&mons[singleobj->corpsenm])) {
356 impossible("monster throwing egg type %d",
357 singleobj->corpsenm);
364 hitu = thitu(8, 0, singleobj, (char *)0);
367 dam = dmgval(singleobj, &youmonst);
368 hitv = 3 - distmin(u.ux,u.uy, mon->mx,mon->my);
369 if (hitv < -4) hitv = -4;
370 if (is_elf(mon->data) &&
371 objects[singleobj->otyp].oc_skill == P_BOW) {
374 MON_WEP(mon)->otyp == ELVEN_BOW)
376 if(singleobj->otyp == ELVEN_ARROW) dam++;
378 if (bigmonst(youmonst.data)) hitv++;
379 hitv += 8 + singleobj->spe;
380 if (dam < 1) dam = 1;
381 hitu = thitu(hitv, dam, singleobj, (char *)0);
383 if (hitu && singleobj->opoisoned &&
384 is_poisonable(singleobj)) {
385 char onmbuf[BUFSZ], knmbuf[BUFSZ];
387 Strcpy(onmbuf, xname(singleobj));
388 Strcpy(knmbuf, killer_xname(singleobj));
389 poisoned(onmbuf, A_STR, knmbuf, -10);
392 can_blnd((struct monst*)0, &youmonst,
393 (uchar)(singleobj->otyp == BLINDING_VENOM ?
394 AT_SPIT : AT_WEAP), singleobj)) {
396 if(singleobj->otyp == CREAM_PIE) {
397 if(!Blind) pline("Yecch! You've been creamed.");
398 else pline("There's %s sticky all over your %s.",
401 } else if(singleobj->otyp == BLINDING_VENOM) {
402 int num_eyes = eyecount(youmonst.data);
403 /* venom in the eyes */
404 if(!Blind) pline_The("venom blinds you.");
405 else Your("%s sting%s.",
406 (num_eyes == 1) ? body_part(EYE) :
407 makeplural(body_part(EYE)),
408 (num_eyes == 1) ? "s" : "");
411 if (hitu && singleobj->otyp == EGG) {
412 if (!Stone_resistance
413 && !(poly_when_stoned(youmonst.data) &&
414 polymon(PM_STONE_GOLEM))) {
420 if (hitu || !range) {
421 (void) drop_throw(singleobj, hitu, u.ux, u.uy);
424 } else if (!range /* reached end of path */
425 /* missile hits edge of screen */
426 || !isok(bhitpos.x+dx,bhitpos.y+dy)
427 /* missile hits the wall */
428 || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ)
429 /* missile hit closed door */
430 || closed_door(bhitpos.x+dx, bhitpos.y+dy)
431 /* missile might hit iron bars */
432 || (levl[bhitpos.x+dx][bhitpos.y+dy].typ == IRONBARS &&
433 hits_bars(&singleobj, bhitpos.x, bhitpos.y, !rn2(5), 0))
435 /* Thrown objects "sink" */
436 || IS_SINK(levl[bhitpos.x][bhitpos.y].typ)
439 if (singleobj) /* hits_bars might have destroyed it */
440 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
443 tmp_at(bhitpos.x, bhitpos.y);
446 tmp_at(bhitpos.x, bhitpos.y);
451 u.ucreamed += blindinc;
452 make_blinded(Blinded + (long)blindinc, FALSE);
453 if (!Blind) Your(vision_clears);
460 /* Remove an item from the monster's inventory and destroy it. */
466 if (obj->quan > 1L) {
468 obj->owt = weight(obj);
470 obj_extract_self(obj);
471 possibly_unwield(mon, FALSE);
472 if (obj->owornmask) {
473 mon->misc_worn_check &= ~obj->owornmask;
474 update_mon_intrinsics(mon, obj, FALSE, FALSE);
476 obfree(obj, (struct obj*) 0);
483 /* monster attempts ranged weapon attack against player */
488 struct obj *otmp, *mwep;
494 /* Rearranged beginning so monsters can use polearms not in a line */
495 if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
496 mtmp->weapon_check = NEED_RANGED_WEAPON;
497 /* mon_wield_item resets weapon_check as appropriate */
498 if(mon_wield_item(mtmp) != 0) return;
502 otmp = select_rwep(mtmp);
508 if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) > POLE_LIM ||
509 !couldsee(mtmp->mx, mtmp->my))
510 return; /* Out of range, or intervening wall */
512 if (canseemon(mtmp)) {
514 pline("%s thrusts %s.", Monnam(mtmp),
515 obj_is_pname(otmp) ? the(onm) : an(onm));
518 dam = dmgval(otmp, &youmonst);
519 hitv = 3 - distmin(u.ux,u.uy, mtmp->mx,mtmp->my);
520 if (hitv < -4) hitv = -4;
521 if (bigmonst(youmonst.data)) hitv++;
522 hitv += 8 + otmp->spe;
523 if (dam < 1) dam = 1;
525 (void) thitu(hitv, dam, otmp, (char *)0);
532 /* If you are coming toward the monster, the monster
533 * should try to soften you up with missiles. If you are
534 * going away, you are probably hurt or running. Give
535 * chase, but if you are getting too far away, throw.
537 if (!lined_up(mtmp) ||
539 rn2(BOLT_LIM - distmin(x,y,mtmp->mux,mtmp->muy))))
542 skill = objects[otmp->otyp].oc_skill;
543 mwep = MON_WEP(mtmp); /* wielded weapon */
545 /* Multishot calculations */
547 if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER ||
548 skill == -P_DART || skill == -P_SHURIKEN) && !mtmp->mconf) {
549 /* Assumes lords are skilled, princes are expert */
550 if (is_prince(mtmp->data)) multishot += 2;
551 else if (is_lord(mtmp->data)) multishot++;
553 switch (monsndx(mtmp->data)) {
558 if (skill == P_DAGGER) multishot++;
562 if (otmp->otyp == YA && mwep &&
563 mwep->otyp == YUMI) multishot++;
569 if ((is_elf(mtmp->data) &&
570 otmp->otyp == ELVEN_ARROW &&
571 mwep && mwep->otyp == ELVEN_BOW) ||
572 (is_orc(mtmp->data) &&
573 otmp->otyp == ORCISH_ARROW &&
574 mwep && mwep->otyp == ORCISH_BOW))
577 if ((long)multishot > otmp->quan) multishot = (int)otmp->quan;
578 if (multishot < 1) multishot = 1;
579 else multishot = rnd(multishot);
582 if (canseemon(mtmp)) {
586 /* "N arrows"; multishot > 1 implies otmp->quan > 1, so
587 xname()'s result will already be pluralized */
588 Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
592 onm = singular(otmp, xname);
593 onm = obj_is_pname(otmp) ? the(onm) : an(onm);
595 m_shot.s = ammo_and_launcher(otmp,mwep) ? TRUE : FALSE;
596 pline("%s %s %s!", Monnam(mtmp),
597 m_shot.s ? "shoots" : "throws", onm);
598 m_shot.o = otmp->otyp;
600 m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */
603 m_shot.n = multishot;
604 for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++)
605 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
606 distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
607 m_shot.n = m_shot.i = 0;
608 m_shot.o = STRANGE_OBJECT;
618 spitmu(mtmp, mattk) /* monster spits substance at you */
619 register struct monst *mtmp;
620 register struct attack *mattk;
622 register struct obj *otmp;
627 pline("A dry rattle comes from %s throat.",
628 s_suffix(mon_nam(mtmp)));
632 switch (mattk->adtyp) {
635 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
638 impossible("bad attack type in spitmu");
641 otmp = mksobj(ACID_VENOM, TRUE, FALSE);
644 if(!rn2(BOLT_LIM-distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy))) {
646 pline("%s spits venom!", Monnam(mtmp));
647 m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
648 distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy), otmp);
660 breamu(mtmp, mattk) /* monster breathes at you (ranged) */
661 register struct monst *mtmp;
662 register struct attack *mattk;
664 /* if new breath types are added, change AD_ACID to max type */
665 int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp ;
672 pline("%s coughs.", Monnam(mtmp));
674 You_hear("a cough.");
678 if(!mtmp->mspec_used && rn2(3)) {
680 if((typ >= AD_MAGM) && (typ <= AD_ACID)) {
683 pline("%s breathes %s!", Monnam(mtmp),
685 buzz((int) (-20 - (typ-1)), (int)mattk->damn,
686 mtmp->mx, mtmp->my, sgn(tbx), sgn(tby));
688 /* breath runs out sometimes. Also, give monster some
689 * cunning; don't breath if the player fell asleep.
692 mtmp->mspec_used = 10+rn2(20);
693 if(typ == AD_SLEE && !Sleep_resistance)
694 mtmp->mspec_used += rnd(20);
695 } else impossible("Breath weapon %d used", typ-1);
702 linedup(ax, ay, bx, by)
703 register xchar ax, ay, bx, by;
705 tbx = ax - bx; /* These two values are set for use */
706 tby = ay - by; /* after successful return. */
708 /* sometimes displacement makes a monster think that you're at its
709 own location; prevent it from throwing and zapping in that case */
710 if (!tbx && !tby) return FALSE;
712 if((!tbx || !tby || abs(tbx) == abs(tby)) /* straight line or diagonal */
713 && distmin(tbx, tby, 0, 0) < BOLT_LIM) {
714 if(ax == u.ux && ay == u.uy) return((boolean)(couldsee(bx,by)));
715 else if(clear_path(ax,ay,bx,by)) return TRUE;
721 lined_up(mtmp) /* is mtmp in position to use ranged attack? */
722 register struct monst *mtmp;
724 return(linedup(mtmp->mux,mtmp->muy,mtmp->mx,mtmp->my));
730 /* Check if a monster is carrying a particular item.
733 m_carrying(mtmp, type)
737 register struct obj *otmp;
739 for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
740 if(otmp->otyp == type)
742 return((struct obj *) 0);
745 /* TRUE iff thrown/kicked/rolled object doesn't pass through iron bars */
747 hits_bars(obj_p, x, y, always_hit, whodidit)
748 struct obj **obj_p; /* *obj_p will be set to NULL if object breaks */
750 int always_hit; /* caller can force a hit for items which would fit through */
751 int whodidit; /* 1==hero, 0=other, -1==just check whether it'll pass thru */
753 struct obj *otmp = *obj_p;
754 int obj_type = otmp->otyp;
755 boolean hits = always_hit;
758 switch (otmp->oclass) {
761 int oskill = objects[obj_type].oc_skill;
763 hits = (oskill != -P_BOW && oskill != -P_CROSSBOW &&
764 oskill != -P_DART && oskill != -P_SHURIKEN &&
765 oskill != P_SPEAR && oskill != P_JAVELIN &&
766 oskill != P_KNIFE); /* but not dagger */
770 hits = (objects[obj_type].oc_armcat != ARM_GLOVES);
773 hits = (obj_type != SKELETON_KEY &&
774 obj_type != LOCK_PICK &&
776 obj_type != CREDIT_CARD &&
778 obj_type != TALLOW_CANDLE &&
779 obj_type != WAX_CANDLE &&
780 obj_type != LENSES &&
781 obj_type != TIN_WHISTLE &&
782 obj_type != MAGIC_WHISTLE);
784 case ROCK_CLASS: /* includes boulder */
785 if (obj_type != STATUE ||
786 mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE;
789 if (obj_type == CORPSE &&
790 mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE;
792 hits = (obj_type == MEAT_STICK ||
793 obj_type == HUGE_CHUNK_OF_MEAT);
805 if (hits && whodidit != -1) {
806 if (whodidit ? hero_breaks(otmp, x, y, FALSE) : breaks(otmp, x, y))
807 *obj_p = otmp = 0; /* object is now gone */
808 /* breakage makes its own noises */
809 else if (obj_type == BOULDER || obj_type == HEAVY_IRON_BALL)
811 else if (otmp->oclass == COIN_CLASS ||
812 objects[obj_type].oc_material == GOLD ||
813 objects[obj_type].oc_material == SILVER)