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. */ 4 5#include "hack.h" 6 7STATIC_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 15STATIC_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 */ 22STATIC_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/* hero is hit by something other than a monster */ 36int 37thitu(tlev, dam, obj, name) 38int tlev, dam; 39struct obj *obj; 40const char *name; /* if null, then format `obj' */ 41{ 42 const char *onm, *knm; 43 boolean is_acid; 44 int kprefix = KILLED_BY_AN; 45 char onmbuf[BUFSZ], knmbuf[BUFSZ]; 46 47 if (!name) { 48 if (!obj) panic("thitu: name & obj both null?"); 49 name = strcpy(onmbuf, 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 */ 53 } else { 54 knm = name; 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; 59 } 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); 63 64 if(u.uac + tlev <= rnd(20)) { 65 if(Blind || !flags.verbose) pline("It misses."); 66 else You("are almost hit by %s.", onm); 67 return(0); 68 } else { 69 if(Blind || !flags.verbose) You("are hit!"); 70 else You("are hit by %s%s", onm, exclam(dam)); 71 72 if (obj && objects[obj->otyp].oc_material == SILVER 73 && hates_silver(youmonst.data)) { 74 dam += rnd(20); 75 pline_The("silver sears your flesh!"); 76 exercise(A_CON, FALSE); 77 } 78 if (is_acid && Acid_resistance) 79 pline("It doesn't seem to hurt you."); 80 else { 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); 85 } 86 return(1); 87 } 88} 89 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). 93 */ 94 95STATIC_OVL int 96drop_throw(obj, ohit, x, y) 97register struct obj *obj; 98boolean ohit; 99int x,y; 100{ 101 int retvalu = 1; 102 int create; 103 struct monst *mtmp; 104 struct trap *t; 105 106 if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS || 107 (ohit && obj->otyp == EGG)) 108 create = 0; 109 else if (ohit && (is_multigen(obj) || obj->otyp == ROCK)) 110 create = !rn2(3); 111 else create = 1; 112 113 if (create && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) && 114 (t = t_at(x, y)) && ((t->ttyp == PIT) || 115 (t->ttyp == SPIKED_PIT)))) { 116 int objgone = 0; 117 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")) { /* don't double-dip on damage */ 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; 129 } 130 } 131 } else obfree(obj, (struct obj*) 0); 132 return retvalu; 133} 134 135#endif /* OVLB */ 136#ifdef OVL1 137 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) */ 140int 141ohitmon(mtmp, otmp, range, verbose) 142struct monst *mtmp; /* accidental target */ 143struct obj *otmp; /* missile; might be destroyed by drop_throw */ 144int 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) */ 147boolean verbose; /* give message(s) even when you can't see what happened */ 148{ 149 int damage, tmp; 150 boolean vis, ismimic; 151 int objgone = 1; 152 153 ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER; 154 vis = cansee(bhitpos.x, bhitpos.y); 155 156 tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE); 157 if (tmp < rnd(20)) { 158 if (!ismimic) { 159 if (vis) miss(distant_name(otmp, mshot_xname), mtmp); 160 else if (verbose) pline("It is missed."); 161 } 162 if (!range) { /* Last position; object drops */ 163 (void) drop_throw(otmp, 0, mtmp->mx, mtmp->my); 164 return 1; 165 } 166 } else if (otmp->oclass == POTION_CLASS) { 167 if (ismimic) seemimic(mtmp); 168 mtmp->msleeping = 0; 169 if (vis) otmp->dknown = 1; 170 potionhit(mtmp, otmp, FALSE); 171 return 1; 172 } else { 173 damage = dmgval(otmp, mtmp); 174 if (otmp->otyp == ACID_VENOM && resists_acid(mtmp)) 175 damage = 0; 176 if (ismimic) seemimic(mtmp); 177 mtmp->msleeping = 0; 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)); 180 181 if (otmp->opoisoned && is_poisonable(otmp)) { 182 if (resists_poison(mtmp)) { 183 if (vis) pline_The("poison doesn't seem to affect %s.", 184 mon_nam(mtmp)); 185 } else { 186 if (rn2(30)) { 187 damage += rnd(6); 188 } else { 189 if (vis) pline_The("poison was deadly..."); 190 damage = mtmp->mhp; 191 } 192 } 193 } 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!"); 199 } 200 if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx,mtmp->my)) { 201 if (resists_acid(mtmp)) { 202 if (vis || verbose) 203 pline("%s is unaffected.", Monnam(mtmp)); 204 damage = 0; 205 } else { 206 if (vis) pline_The("acid burns %s!", mon_nam(mtmp)); 207 else if (verbose) pline("It is burned!"); 208 } 209 } 210 mtmp->mhp -= damage; 211 if (mtmp->mhp < 1) { 212 if (vis || verbose) 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)) 219 xkilled(mtmp,0); 220 else mondied(mtmp); 221 } 222 223 if (can_blnd((struct monst*)0, mtmp, 224 (uchar)(otmp->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP), 225 otmp)) { 226 if (vis && mtmp->mcansee) 227 pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp))); 228 mtmp->mcansee = 0; 229 tmp = (int)mtmp->mblinded + rnd(25) + 20; 230 if (tmp > 127) tmp = 127; 231 mtmp->mblinded = tmp; 232 } 233 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 */ 237 return 0; 238 } 239 return 1; 240 } 241 return 0; 242} 243 244void 245m_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; 249{ 250 register struct monst *mtmp; 251 struct obj *singleobj; 252 char sym = obj->oclass; 253 int hitu, blindinc = 0; 254 255 bhitpos.x = x; 256 bhitpos.y = y; 257 258 if (obj->quan == 1L) { 259 /* 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 263 * dagger bug). 264 * 265 * VENOM is not in minvent - it should already be OBJ_FREE. 266 * The extract below does nothing. 267 */ 268 269 /* not possibly_unwield, which checks the object's */ 270 /* location, not its existence */ 271 if (MON_WEP(mon) == obj) { 272 setmnotwielded(mon,obj); 273 MON_NOWEP(mon); 274 } 275 obj_extract_self(obj); 276 singleobj = obj; 277 obj = (struct obj *) 0; 278 } else { 279 singleobj = splitobj(obj, 1L); 280 obj_extract_self(singleobj); 281 } 282 283 singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */ 284 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)); 289 else 290 pline("%s as %s throws it!", 291 Tobjnam(singleobj, "slip"), mon_nam(mon)); 292 } 293 dx = rn2(3)-1; 294 dy = rn2(3)-1; 295 /* check validity of new direction */ 296 if (!dx && !dy) { 297 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); 298 return; 299 } 300 } 301 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); 312 return; 313 } 314 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. 318 */ 319 if (sym) tmp_at(DISP_FLASH, obj_to_glyph(singleobj)); 320 while(range-- > 0) { /* Actually the loop is always exited by break */ 321 bhitpos.x += dx; 322 bhitpos.y += dy; 323 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) { 324 if (ohitmon(mtmp, singleobj, range, TRUE)) 325 break; 326 } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) { 327 if (multi) nomul(0); 328 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); 337 dropy(singleobj); 338 } else { 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), 343 "You catch:"); 344 } 345 break; 346 } 347 if (singleobj->oclass == POTION_CLASS) { 348 if (!Blind) singleobj->dknown = 1; 349 potionhit(&youmonst, singleobj, FALSE); 350 break; 351 } 352 switch(singleobj->otyp) { 353 int dam, hitv; 354 case EGG: 355 if (!touch_petrifies(&mons[singleobj->corpsenm])) { 356 impossible("monster throwing egg type %d", 357 singleobj->corpsenm); 358 hitu = 0; 359 break; 360 } 361 /* fall through */ 362 case CREAM_PIE: 363 case BLINDING_VENOM: 364 hitu = thitu(8, 0, singleobj, (char *)0); 365 break; 366 default: 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) { 372 hitv++; 373 if (MON_WEP(mon) && 374 MON_WEP(mon)->otyp == ELVEN_BOW) 375 hitv++; 376 if(singleobj->otyp == ELVEN_ARROW) dam++; 377 } 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); 382 } 383 if (hitu && singleobj->opoisoned && 384 is_poisonable(singleobj)) { 385 char onmbuf[BUFSZ], knmbuf[BUFSZ]; 386 387 Strcpy(onmbuf, xname(singleobj)); 388 Strcpy(knmbuf, killer_xname(singleobj)); 389 poisoned(onmbuf, A_STR, knmbuf, -10); 390 } 391 if(hitu && 392 can_blnd((struct monst*)0, &youmonst, 393 (uchar)(singleobj->otyp == BLINDING_VENOM ? 394 AT_SPIT : AT_WEAP), singleobj)) { 395 blindinc = rnd(25); 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.", 399 something, 400 body_part(FACE)); 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" : ""); 409 } 410 } 411 if (hitu && singleobj->otyp == EGG) { 412 if (!Stone_resistance 413 && !(poly_when_stoned(youmonst.data) && 414 polymon(PM_STONE_GOLEM))) { 415 Stoned = 5; 416 killer = (char *) 0; 417 } 418 } 419 stop_occupation(); 420 if (hitu || !range) { 421 (void) drop_throw(singleobj, hitu, u.ux, u.uy); 422 break; 423 } 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)) 434#ifdef SINKS 435 /* Thrown objects "sink" */ 436 || IS_SINK(levl[bhitpos.x][bhitpos.y].typ) 437#endif 438 ) { 439 if (singleobj) /* hits_bars might have destroyed it */ 440 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); 441 break; 442 } 443 tmp_at(bhitpos.x, bhitpos.y); 444 delay_output(); 445 } 446 tmp_at(bhitpos.x, bhitpos.y); 447 delay_output(); 448 tmp_at(DISP_END, 0); 449 450 if (blindinc) { 451 u.ucreamed += blindinc; 452 make_blinded(Blinded + (long)blindinc, FALSE); 453 if (!Blind) Your(vision_clears); 454 } 455} 456 457#endif /* OVL1 */ 458#ifdef OVLB 459 460/* Remove an item from the monster's inventory and destroy it. */ 461void 462m_useup(mon, obj) 463struct monst *mon; 464struct obj *obj; 465{ 466 if (obj->quan > 1L) { 467 obj->quan--; 468 obj->owt = weight(obj); 469 } else { 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); 475 } 476 obfree(obj, (struct obj*) 0); 477 } 478} 479 480#endif /* OVLB */ 481#ifdef OVL1 482 483/* monster attempts ranged weapon attack against player */ 484void 485thrwmu(mtmp) 486struct monst *mtmp; 487{ 488 struct obj *otmp, *mwep; 489 xchar x, y; 490 schar skill; 491 int multishot; 492 const char *onm; 493 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; 499 } 500 501 /* Pick a weapon */ 502 otmp = select_rwep(mtmp); 503 if (!otmp) return; 504 505 if (is_pole(otmp)) { 506 int dam, hitv; 507 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 */ 511 512 if (canseemon(mtmp)) { 513 onm = xname(otmp); 514 pline("%s thrusts %s.", Monnam(mtmp), 515 obj_is_pname(otmp) ? the(onm) : an(onm)); 516 } 517 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; 524 525 (void) thitu(hitv, dam, otmp, (char *)0); 526 stop_occupation(); 527 return; 528 } 529 530 x = mtmp->mx; 531 y = mtmp->my; 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. 536 */ 537 if (!lined_up(mtmp) || 538 (URETREATING(x,y) && 539 rn2(BOLT_LIM - distmin(x,y,mtmp->mux,mtmp->muy)))) 540 return; 541 542 skill = objects[otmp->otyp].oc_skill; 543 mwep = MON_WEP(mtmp); /* wielded weapon */ 544 545 /* Multishot calculations */ 546 multishot = 1; 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++; 552 553 switch (monsndx(mtmp->data)) { 554 case PM_RANGER: 555 multishot++; 556 break; 557 case PM_ROGUE: 558 if (skill == P_DAGGER) multishot++; 559 break; 560 case PM_NINJA: 561 case PM_SAMURAI: 562 if (otmp->otyp == YA && mwep && 563 mwep->otyp == YUMI) multishot++; 564 break; 565 default: 566 break; 567 } 568 /* racial bonus */ 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)) 575 multishot++; 576 577 if ((long)multishot > otmp->quan) multishot = (int)otmp->quan; 578 if (multishot < 1) multishot = 1; 579 else multishot = rnd(multishot); 580 } 581 582 if (canseemon(mtmp)) { 583 char onmbuf[BUFSZ]; 584 585 if (multishot > 1) { 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)); 589 onm = onmbuf; 590 } else { 591 /* "an arrow" */ 592 onm = singular(otmp, xname); 593 onm = obj_is_pname(otmp) ? the(onm) : an(onm); 594 } 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; 599 } else { 600 m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */ 601 } 602 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; 609 m_shot.s = FALSE; 610 611 nomul(0); 612} 613 614#endif /* OVL1 */ 615#ifdef OVLB 616 617int 618spitmu(mtmp, mattk) /* monster spits substance at you */ 619register struct monst *mtmp; 620register struct attack *mattk; 621{ 622 register struct obj *otmp; 623 624 if(mtmp->mcan) { 625 626 if(flags.soundok) 627 pline("A dry rattle comes from %s throat.", 628 s_suffix(mon_nam(mtmp))); 629 return 0; 630 } 631 if(lined_up(mtmp)) { 632 switch (mattk->adtyp) { 633 case AD_BLND: 634 case AD_DRST: 635 otmp = mksobj(BLINDING_VENOM, TRUE, FALSE); 636 break; 637 default: 638 impossible("bad attack type in spitmu"); 639 /* fall through */ 640 case AD_ACID: 641 otmp = mksobj(ACID_VENOM, TRUE, FALSE); 642 break; 643 } 644 if(!rn2(BOLT_LIM-distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy))) { 645 if (canseemon(mtmp)) 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); 649 nomul(0); 650 return 0; 651 } 652 } 653 return 0; 654} 655 656#endif /* OVLB */ 657#ifdef OVL1 658 659int 660breamu(mtmp, mattk) /* monster breathes at you (ranged) */ 661 register struct monst *mtmp; 662 register struct attack *mattk; 663{ 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 ; 666 667 if(lined_up(mtmp)) { 668 669 if(mtmp->mcan) { 670 if(flags.soundok) { 671 if(canseemon(mtmp)) 672 pline("%s coughs.", Monnam(mtmp)); 673 else 674 You_hear("a cough."); 675 } 676 return(0); 677 } 678 if(!mtmp->mspec_used && rn2(3)) { 679 680 if((typ >= AD_MAGM) && (typ <= AD_ACID)) { 681 682 if(canseemon(mtmp)) 683 pline("%s breathes %s!", Monnam(mtmp), 684 breathwep[typ-1]); 685 buzz((int) (-20 - (typ-1)), (int)mattk->damn, 686 mtmp->mx, mtmp->my, sgn(tbx), sgn(tby)); 687 nomul(0); 688 /* breath runs out sometimes. Also, give monster some 689 * cunning; don't breath if the player fell asleep. 690 */ 691 if(!rn2(3)) 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); 696 } 697 } 698 return(1); 699} 700 701boolean 702linedup(ax, ay, bx, by) 703register xchar ax, ay, bx, by; 704{ 705 tbx = ax - bx; /* These two values are set for use */ 706 tby = ay - by; /* after successful return. */ 707 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; 711 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; 716 } 717 return FALSE; 718} 719 720boolean 721lined_up(mtmp) /* is mtmp in position to use ranged attack? */ 722 register struct monst *mtmp; 723{ 724 return(linedup(mtmp->mux,mtmp->muy,mtmp->mx,mtmp->my)); 725} 726 727#endif /* OVL1 */ 728#ifdef OVL0 729 730/* Check if a monster is carrying a particular item. 731 */ 732struct obj * 733m_carrying(mtmp, type) 734struct monst *mtmp; 735int type; 736{ 737 register struct obj *otmp; 738 739 for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj) 740 if(otmp->otyp == type) 741 return(otmp); 742 return((struct obj *) 0); 743} 744 745/* TRUE iff thrown/kicked/rolled object doesn't pass through iron bars */ 746boolean 747hits_bars(obj_p, x, y, always_hit, whodidit) 748struct obj **obj_p; /* *obj_p will be set to NULL if object breaks */ 749int x, y; 750int always_hit; /* caller can force a hit for items which would fit through */ 751int whodidit; /* 1==hero, 0=other, -1==just check whether it'll pass thru */ 752{ 753 struct obj *otmp = *obj_p; 754 int obj_type = otmp->otyp; 755 boolean hits = always_hit; 756 757 if (!hits) 758 switch (otmp->oclass) { 759 case WEAPON_CLASS: 760 { 761 int oskill = objects[obj_type].oc_skill; 762 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 */ 767 break; 768 } 769 case ARMOR_CLASS: 770 hits = (objects[obj_type].oc_armcat != ARM_GLOVES); 771 break; 772 case TOOL_CLASS: 773 hits = (obj_type != SKELETON_KEY && 774 obj_type != LOCK_PICK && 775#ifdef TOURIST 776 obj_type != CREDIT_CARD && 777#endif 778 obj_type != TALLOW_CANDLE && 779 obj_type != WAX_CANDLE && 780 obj_type != LENSES && 781 obj_type != TIN_WHISTLE && 782 obj_type != MAGIC_WHISTLE); 783 break; 784 case ROCK_CLASS: /* includes boulder */ 785 if (obj_type != STATUE || 786 mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE; 787 break; 788 case FOOD_CLASS: 789 if (obj_type == CORPSE && 790 mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE; 791 else 792 hits = (obj_type == MEAT_STICK || 793 obj_type == HUGE_CHUNK_OF_MEAT); 794 break; 795 case SPBOOK_CLASS: 796 case WAND_CLASS: 797 case BALL_CLASS: 798 case CHAIN_CLASS: 799 hits = TRUE; 800 break; 801 default: 802 break; 803 } 804 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) 810 pline("Whang!"); 811 else if (otmp->oclass == COIN_CLASS || 812 objects[obj_type].oc_material == GOLD || 813 objects[obj_type].oc_material == SILVER) 814 pline("Clink!"); 815 else 816 pline("Clonk!"); 817 } 818 819 return hits; 820} 821 822#endif /* OVL0 */ 823 824/*mthrowu.c*/ 825