1/* SCCS Id: @(#)dothrow.c 3.4 2003/12/04 */ 2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5/* Contains code for 't' (throw) */ 6 7#include "hack.h" 8#include "edog.h" 9 10STATIC_DCL int FDECL(throw_obj, (struct obj *,int)); 11STATIC_DCL void NDECL(autoquiver); 12STATIC_DCL int FDECL(gem_accept, (struct monst *, struct obj *)); 13STATIC_DCL void FDECL(tmiss, (struct obj *, struct monst *)); 14STATIC_DCL int FDECL(throw_gold, (struct obj *)); 15STATIC_DCL void FDECL(check_shop_obj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P)); 16STATIC_DCL void FDECL(breakobj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P,BOOLEAN_P)); 17STATIC_DCL void FDECL(breakmsg, (struct obj *,BOOLEAN_P)); 18STATIC_DCL boolean FDECL(toss_up,(struct obj *, BOOLEAN_P)); 19STATIC_DCL boolean FDECL(throwing_weapon, (struct obj *)); 20STATIC_DCL void FDECL(sho_obj_return_to_u, (struct obj *obj)); 21STATIC_DCL boolean FDECL(mhurtle_step, (genericptr_t,int,int)); 22 23 24static NEARDATA const char toss_objs[] = 25 { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, WEAPON_CLASS, 0 }; 26/* different default choices when wielding a sling (gold must be included) */ 27static NEARDATA const char bullets[] = 28 { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, GEM_CLASS, 0 }; 29 30struct obj *thrownobj = 0; /* tracks an object until it lands */ 31 32extern boolean notonhead; /* for long worms */ 33 34 35/* Throw the selected object, asking for direction */ 36STATIC_OVL int 37throw_obj(obj, shotlimit) 38struct obj *obj; 39int shotlimit; 40{ 41 struct obj *otmp; 42 int multishot = 1; 43 schar skill; 44 long wep_mask; 45 boolean twoweap; 46 47 /* ask "in what direction?" */ 48#ifndef GOLDOBJ 49 if (!getdir((char *)0)) { 50 if (obj->oclass == COIN_CLASS) { 51 u.ugold += obj->quan; 52 flags.botl = 1; 53 dealloc_obj(obj); 54 } 55 return(0); 56 } 57 58 if(obj->oclass == COIN_CLASS) return(throw_gold(obj)); 59#else 60 if (!getdir((char *)0)) { 61 /* obj might need to be merged back into the singular gold object */ 62 freeinv(obj); 63 addinv(obj); 64 return(0); 65 } 66 67 /* 68 Throwing money is usually for getting rid of it when 69 a leprechaun approaches, or for bribing an oncoming 70 angry monster. So throw the whole object. 71 72 If the money is in quiver, throw one coin at a time, 73 possibly using a sling. 74 */ 75 if(obj->oclass == COIN_CLASS && obj != uquiver) return(throw_gold(obj)); 76#endif 77 78 if(!canletgo(obj,"throw")) 79 return(0); 80 if (obj->oartifact == ART_MJOLLNIR && obj != uwep) { 81 pline("%s must be wielded before it can be thrown.", 82 The(xname(obj))); 83 return(0); 84 } 85 if ((obj->oartifact == ART_MJOLLNIR && ACURR(A_STR) < STR19(25)) 86 || (obj->otyp == BOULDER && !throws_rocks(youmonst.data))) { 87 pline("It's too heavy."); 88 return(1); 89 } 90 if(!u.dx && !u.dy && !u.dz) { 91 You("cannot throw an object at yourself."); 92 return(0); 93 } 94 u_wipe_engr(2); 95 if (!uarmg && !Stone_resistance && (obj->otyp == CORPSE && 96 touch_petrifies(&mons[obj->corpsenm]))) { 97 You("throw the %s corpse with your bare %s.", 98 mons[obj->corpsenm].mname, body_part(HAND)); 99 Sprintf(killer_buf, "%s corpse", an(mons[obj->corpsenm].mname)); 100 instapetrify(killer_buf); 101 } 102 if (welded(obj)) { 103 weldmsg(obj); 104 return 1; 105 } 106 107 /* Multishot calculations 108 */ 109 skill = objects[obj->otyp].oc_skill; 110 if ((ammo_and_launcher(obj, uwep) || skill == P_DAGGER || 111 skill == -P_DART || skill == -P_SHURIKEN) && 112 !(Confusion || Stunned)) { 113 /* Bonus if the player is proficient in this weapon... */ 114 switch (P_SKILL(weapon_type(obj))) { 115 default: break; /* No bonus */ 116 case P_SKILLED: multishot++; break; 117 case P_EXPERT: multishot += 2; break; 118 } 119 /* ...or is using a special weapon for their role... */ 120 switch (Role_switch) { 121 case PM_RANGER: 122 multishot++; 123 break; 124 case PM_ROGUE: 125 if (skill == P_DAGGER) multishot++; 126 break; 127 case PM_SAMURAI: 128 if (obj->otyp == YA && uwep && uwep->otyp == YUMI) multishot++; 129 break; 130 default: 131 break; /* No bonus */ 132 } 133 /* ...or using their race's special bow */ 134 switch (Race_switch) { 135 case PM_ELF: 136 if (obj->otyp == ELVEN_ARROW && uwep && 137 uwep->otyp == ELVEN_BOW) multishot++; 138 break; 139 case PM_ORC: 140 if (obj->otyp == ORCISH_ARROW && uwep && 141 uwep->otyp == ORCISH_BOW) multishot++; 142 break; 143 default: 144 break; /* No bonus */ 145 } 146 } 147 148 if ((long)multishot > obj->quan) multishot = (int)obj->quan; 149 multishot = rnd(multishot); 150 if (shotlimit > 0 && multishot > shotlimit) multishot = shotlimit; 151 152 m_shot.s = ammo_and_launcher(obj,uwep) ? TRUE : FALSE; 153 /* give a message if shooting more than one, or if player 154 attempted to specify a count */ 155 if (multishot > 1 || shotlimit > 0) { 156 /* "You shoot N arrows." or "You throw N daggers." */ 157 You("%s %d %s.", 158 m_shot.s ? "shoot" : "throw", 159 multishot, /* (might be 1 if player gave shotlimit) */ 160 (multishot == 1) ? singular(obj, xname) : xname(obj)); 161 } 162 163 wep_mask = obj->owornmask; 164 m_shot.o = obj->otyp; 165 m_shot.n = multishot; 166 for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) { 167 twoweap = u.twoweap; 168 /* split this object off from its slot if necessary */ 169 if (obj->quan > 1L) { 170 otmp = splitobj(obj, 1L); 171 } else { 172 otmp = obj; 173 if (otmp->owornmask) 174 remove_worn_item(otmp, FALSE); 175 } 176 freeinv(otmp); 177 throwit(otmp, wep_mask, twoweap); 178 } 179 m_shot.n = m_shot.i = 0; 180 m_shot.o = STRANGE_OBJECT; 181 m_shot.s = FALSE; 182 183 return 1; 184} 185 186 187int 188dothrow() 189{ 190 register struct obj *obj; 191 int shotlimit; 192 193 /* 194 * Since some characters shoot multiple missiles at one time, 195 * allow user to specify a count prefix for 'f' or 't' to limit 196 * number of items thrown (to avoid possibly hitting something 197 * behind target after killing it, or perhaps to conserve ammo). 198 * 199 * Prior to 3.3.0, command ``3t'' meant ``t(shoot) t(shoot) t(shoot)'' 200 * and took 3 turns. Now it means ``t(shoot at most 3 missiles)''. 201 */ 202 /* kludge to work around parse()'s pre-decrement of `multi' */ 203 shotlimit = (multi || save_cm) ? multi + 1 : 0; 204 multi = 0; /* reset; it's been used up */ 205 206 if (notake(youmonst.data)) { 207 You("are physically incapable of throwing anything."); 208 return 0; 209 } 210 211 if(check_capacity((char *)0)) return(0); 212 obj = getobj(uslinging() ? bullets : toss_objs, "throw"); 213 /* it is also possible to throw food */ 214 /* (or jewels, or iron balls... ) */ 215 216 if (!obj) return(0); 217 return throw_obj(obj, shotlimit); 218} 219 220 221/* KMH -- Automatically fill quiver */ 222/* Suggested by Jeffrey Bay <jbay@convex.hp.com> */ 223static void 224autoquiver() 225{ 226 struct obj *otmp, *oammo = 0, *omissile = 0, *omisc = 0, *altammo = 0; 227 228 if (uquiver) 229 return; 230 231 /* Scan through the inventory */ 232 for (otmp = invent; otmp; otmp = otmp->nobj) { 233 if (otmp->owornmask || otmp->oartifact || !otmp->dknown) { 234 ; /* Skip it */ 235 } else if (otmp->otyp == ROCK || 236 /* seen rocks or known flint or known glass */ 237 (objects[otmp->otyp].oc_name_known && 238 otmp->otyp == FLINT) || 239 (objects[otmp->otyp].oc_name_known && 240 otmp->oclass == GEM_CLASS && 241 objects[otmp->otyp].oc_material == GLASS)) { 242 if (uslinging()) 243 oammo = otmp; 244 else if (ammo_and_launcher(otmp, uswapwep)) 245 altammo = otmp; 246 else if (!omisc) 247 omisc = otmp; 248 } else if (otmp->oclass == GEM_CLASS) { 249 ; /* skip non-rock gems--they're ammo but 250 player has to select them explicitly */ 251 } else if (is_ammo(otmp)) { 252 if (ammo_and_launcher(otmp, uwep)) 253 /* Ammo matched with launcher (bow and arrow, crossbow and bolt) */ 254 oammo = otmp; 255 else if (ammo_and_launcher(otmp, uswapwep)) 256 altammo = otmp; 257 else 258 /* Mismatched ammo (no better than an ordinary weapon) */ 259 omisc = otmp; 260 } else if (is_missile(otmp)) { 261 /* Missile (dart, shuriken, etc.) */ 262 omissile = otmp; 263 } else if (otmp->oclass == WEAPON_CLASS && throwing_weapon(otmp)) { 264 /* Ordinary weapon */ 265 if (objects[otmp->otyp].oc_skill == P_DAGGER 266 && !omissile) 267 omissile = otmp; 268 else 269 omisc = otmp; 270 } 271 } 272 273 /* Pick the best choice */ 274 if (oammo) 275 setuqwep(oammo); 276 else if (omissile) 277 setuqwep(omissile); 278 else if (altammo) 279 setuqwep(altammo); 280 else if (omisc) 281 setuqwep(omisc); 282 283 return; 284} 285 286 287/* Throw from the quiver */ 288int 289dofire() 290{ 291 int shotlimit; 292 293 if (notake(youmonst.data)) { 294 You("are physically incapable of doing that."); 295 return 0; 296 } 297 298 if(check_capacity((char *)0)) return(0); 299 if (!uquiver) { 300 if (!flags.autoquiver) { 301 /* Don't automatically fill the quiver */ 302 You("have no ammunition readied!"); 303 return(dothrow()); 304 } 305 autoquiver(); 306 if (!uquiver) { 307 You("have nothing appropriate for your quiver!"); 308 return(dothrow()); 309 } else { 310 You("fill your quiver:"); 311 prinv((char *)0, uquiver, 0L); 312 } 313 } 314 315 /* 316 * Since some characters shoot multiple missiles at one time, 317 * allow user to specify a count prefix for 'f' or 't' to limit 318 * number of items thrown (to avoid possibly hitting something 319 * behind target after killing it, or perhaps to conserve ammo). 320 * 321 * The number specified can never increase the number of missiles. 322 * Using ``5f'' when the shooting skill (plus RNG) dictates launch 323 * of 3 projectiles will result in 3 being shot, not 5. 324 */ 325 /* kludge to work around parse()'s pre-decrement of `multi' */ 326 shotlimit = (multi || save_cm) ? multi + 1 : 0; 327 multi = 0; /* reset; it's been used up */ 328 329 return throw_obj(uquiver, shotlimit); 330} 331 332 333/* 334 * Object hits floor at hero's feet. Called from drop() and throwit(). 335 */ 336void 337hitfloor(obj) 338register struct obj *obj; 339{ 340 if (IS_SOFT(levl[u.ux][u.uy].typ) || u.uinwater) { 341 dropy(obj); 342 return; 343 } 344 if (IS_ALTAR(levl[u.ux][u.uy].typ)) 345 doaltarobj(obj); 346 else 347 pline("%s hit%s the %s.", Doname2(obj), 348 (obj->quan == 1L) ? "s" : "", surface(u.ux,u.uy)); 349 350 if (hero_breaks(obj, u.ux, u.uy, TRUE)) return; 351 if (ship_object(obj, u.ux, u.uy, FALSE)) return; 352 dropy(obj); 353 if (!u.uswallow) container_impact_dmg(obj); 354} 355 356/* 357 * Walk a path from src_cc to dest_cc, calling a proc for each location 358 * except the starting one. If the proc returns FALSE, stop walking 359 * and return FALSE. If stopped early, dest_cc will be the location 360 * before the failed callback. 361 */ 362boolean 363walk_path(src_cc, dest_cc, check_proc, arg) 364 coord *src_cc; 365 coord *dest_cc; 366 boolean FDECL((*check_proc), (genericptr_t, int, int)); 367 genericptr_t arg; 368{ 369 int x, y, dx, dy, x_change, y_change, err, i, prev_x, prev_y; 370 boolean keep_going = TRUE; 371 372 /* Use Bresenham's Line Algorithm to walk from src to dest */ 373 dx = dest_cc->x - src_cc->x; 374 dy = dest_cc->y - src_cc->y; 375 prev_x = x = src_cc->x; 376 prev_y = y = src_cc->y; 377 378 if (dx < 0) { 379 x_change = -1; 380 dx = -dx; 381 } else 382 x_change = 1; 383 if (dy < 0) { 384 y_change = -1; 385 dy = -dy; 386 } else 387 y_change = 1; 388 389 i = err = 0; 390 if (dx < dy) { 391 while (i++ < dy) { 392 prev_x = x; 393 prev_y = y; 394 y += y_change; 395 err += dx; 396 if (err >= dy) { 397 x += x_change; 398 err -= dy; 399 } 400 /* check for early exit condition */ 401 if (!(keep_going = (*check_proc)(arg, x, y))) 402 break; 403 } 404 } else { 405 while (i++ < dx) { 406 prev_x = x; 407 prev_y = y; 408 x += x_change; 409 err += dy; 410 if (err >= dx) { 411 y += y_change; 412 err -= dx; 413 } 414 /* check for early exit condition */ 415 if (!(keep_going = (*check_proc)(arg, x, y))) 416 break; 417 } 418 } 419 420 if (keep_going) 421 return TRUE; /* successful */ 422 423 dest_cc->x = prev_x; 424 dest_cc->y = prev_y; 425 return FALSE; 426} 427 428/* 429 * Single step for the hero flying through the air from jumping, flying, 430 * etc. Called from hurtle() and jump() via walk_path(). We expect the 431 * argument to be a pointer to an integer -- the range -- which is 432 * used in the calculation of points off if we hit something. 433 * 434 * Bumping into monsters won't cause damage but will wake them and make 435 * them angry. Auto-pickup isn't done, since you don't have control over 436 * your movements at the time. 437 * 438 * Possible additions/changes: 439 * o really attack monster if we hit one 440 * o set stunned if we hit a wall or door 441 * o reset nomul when we stop 442 * o creepy feeling if pass through monster (if ever implemented...) 443 * o bounce off walls 444 * o let jumps go over boulders 445 */ 446boolean 447hurtle_step(arg, x, y) 448 genericptr_t arg; 449 int x, y; 450{ 451 int ox, oy, *range = (int *)arg; 452 struct obj *obj; 453 struct monst *mon; 454 boolean may_pass = TRUE; 455 struct trap *ttmp; 456 457 if (!isok(x,y)) { 458 You_feel("the spirits holding you back."); 459 return FALSE; 460 } else if (!in_out_region(x, y)) { 461 return FALSE; 462 } else if (*range == 0) { 463 return FALSE; /* previous step wants to stop now */ 464 } 465 466 if (!Passes_walls || !(may_pass = may_passwall(x, y))) { 467 if (IS_ROCK(levl[x][y].typ) || closed_door(x,y)) { 468 const char *s; 469 470 pline("Ouch!"); 471 if (IS_TREE(levl[x][y].typ)) 472 s = "bumping into a tree"; 473 else if (IS_ROCK(levl[x][y].typ)) 474 s = "bumping into a wall"; 475 else 476 s = "bumping into a door"; 477 losehp(rnd(2+*range), s, KILLED_BY); 478 return FALSE; 479 } 480 if (levl[x][y].typ == IRONBARS) { 481 You("crash into some iron bars. Ouch!"); 482 losehp(rnd(2+*range), "crashing into iron bars", KILLED_BY); 483 return FALSE; 484 } 485 if ((obj = sobj_at(BOULDER,x,y)) != 0) { 486 You("bump into a %s. Ouch!", xname(obj)); 487 losehp(rnd(2+*range), "bumping into a boulder", KILLED_BY); 488 return FALSE; 489 } 490 if (!may_pass) { 491 /* did we hit a no-dig non-wall position? */ 492 You("smack into something!"); 493 losehp(rnd(2+*range), "touching the edge of the universe", KILLED_BY); 494 return FALSE; 495 } 496 if ((u.ux - x) && (u.uy - y) && 497 bad_rock(youmonst.data,u.ux,y) && bad_rock(youmonst.data,x,u.uy)) { 498 boolean too_much = (invent && (inv_weight() + weight_cap() > 600)); 499 /* Move at a diagonal. */ 500 if (bigmonst(youmonst.data) || too_much) { 501 You("%sget forcefully wedged into a crevice.", 502 too_much ? "and all your belongings " : ""); 503 losehp(rnd(2+*range), "wedging into a narrow crevice", KILLED_BY); 504 return FALSE; 505 } 506 } 507 } 508 509 if ((mon = m_at(x, y)) != 0) { 510 You("bump into %s.", a_monnam(mon)); 511 wakeup(mon); 512 return FALSE; 513 } 514 if ((u.ux - x) && (u.uy - y) && 515 bad_rock(youmonst.data,u.ux,y) && bad_rock(youmonst.data,x,u.uy)) { 516 /* Move at a diagonal. */ 517 if (In_sokoban(&u.uz)) { 518 You("come to an abrupt halt!"); 519 return FALSE; 520 } 521 } 522 523 ox = u.ux; 524 oy = u.uy; 525 u.ux = x; 526 u.uy = y; 527 newsym(ox, oy); /* update old position */ 528 vision_recalc(1); /* update for new position */ 529 flush_screen(1); 530 /* FIXME: 531 * Each trap should really trigger on the recoil if 532 * it would trigger during normal movement. However, 533 * not all the possible side-effects of this are 534 * tested [as of 3.4.0] so we trigger those that 535 * we have tested, and offer a message for the 536 * ones that we have not yet tested. 537 */ 538 if ((ttmp = t_at(x, y)) != 0) { 539 if (ttmp->ttyp == MAGIC_PORTAL) { 540 dotrap(ttmp,0); 541 return FALSE; 542 } else if (ttmp->ttyp == FIRE_TRAP) { 543 dotrap(ttmp,0); 544 } else if ((ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT || 545 ttmp->ttyp == HOLE || ttmp->ttyp == TRAPDOOR) && 546 In_sokoban(&u.uz)) { 547 /* Air currents overcome the recoil */ 548 dotrap(ttmp,0); 549 *range = 0; 550 return TRUE; 551 } else { 552 if (ttmp->tseen) 553 You("pass right over %s %s.", 554 (ttmp->ttyp == ARROW_TRAP) ? "an" : "a", 555 defsyms[trap_to_defsym(ttmp->ttyp)].explanation); 556 } 557 } 558 if (--*range < 0) /* make sure our range never goes negative */ 559 *range = 0; 560 if (*range != 0) 561 delay_output(); 562 return TRUE; 563} 564 565STATIC_OVL boolean 566mhurtle_step(arg, x, y) 567 genericptr_t arg; 568 int x, y; 569{ 570 struct monst *mon = (struct monst *)arg; 571 572 /* TODO: Treat walls, doors, iron bars, pools, lava, etc. specially 573 * rather than just stopping before. 574 */ 575 if (goodpos(x, y, mon, 0) && m_in_out_region(mon, x, y)) { 576 remove_monster(mon->mx, mon->my); 577 newsym(mon->mx, mon->my); 578 place_monster(mon, x, y); 579 newsym(mon->mx, mon->my); 580 set_apparxy(mon); 581 (void) mintrap(mon); 582 return TRUE; 583 } 584 return FALSE; 585} 586 587/* 588 * The player moves through the air for a few squares as a result of 589 * throwing or kicking something. 590 * 591 * dx and dy should be the direction of the hurtle, not of the original 592 * kick or throw and be only. 593 */ 594void 595hurtle(dx, dy, range, verbose) 596 int dx, dy, range; 597 boolean verbose; 598{ 599 coord uc, cc; 600 601 /* The chain is stretched vertically, so you shouldn't be able to move 602 * very far diagonally. The premise that you should be able to move one 603 * spot leads to calculations that allow you to only move one spot away 604 * from the ball, if you are levitating over the ball, or one spot 605 * towards the ball, if you are at the end of the chain. Rather than 606 * bother with all of that, assume that there is no slack in the chain 607 * for diagonal movement, give the player a message and return. 608 */ 609 if(Punished && !carried(uball)) { 610 You_feel("a tug from the iron ball."); 611 nomul(0); 612 return; 613 } else if (u.utrap) { 614 You("are anchored by the %s.", 615 u.utraptype == TT_WEB ? "web" : u.utraptype == TT_LAVA ? "lava" : 616 u.utraptype == TT_INFLOOR ? surface(u.ux,u.uy) : "trap"); 617 nomul(0); 618 return; 619 } 620 621 /* make sure dx and dy are [-1,0,1] */ 622 dx = sgn(dx); 623 dy = sgn(dy); 624 625 if(!range || (!dx && !dy) || u.ustuck) return; /* paranoia */ 626 627 nomul(-range); 628 if (verbose) 629 You("%s in the opposite direction.", range > 1 ? "hurtle" : "float"); 630 /* if we're in the midst of shooting multiple projectiles, stop */ 631 if (m_shot.i < m_shot.n) { 632 /* last message before hurtling was "you shoot N arrows" */ 633 You("stop %sing after the first %s.", 634 m_shot.s ? "shoot" : "throw", m_shot.s ? "shot" : "toss"); 635 m_shot.n = m_shot.i; /* make current shot be the last */ 636 } 637 if (In_sokoban(&u.uz)) 638 change_luck(-1); /* Sokoban guilt */ 639 uc.x = u.ux; 640 uc.y = u.uy; 641 /* this setting of cc is only correct if dx and dy are [-1,0,1] only */ 642 cc.x = u.ux + (dx * range); 643 cc.y = u.uy + (dy * range); 644 (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t)&range); 645} 646 647/* Move a monster through the air for a few squares. 648 */ 649void 650mhurtle(mon, dx, dy, range) 651 struct monst *mon; 652 int dx, dy, range; 653{ 654 coord mc, cc; 655 656 /* At the very least, debilitate the monster */ 657 mon->movement = 0; 658 mon->mstun = 1; 659 660 /* Is the monster stuck or too heavy to push? 661 * (very large monsters have too much inertia, even floaters and flyers) 662 */ 663 if (mon->data->msize >= MZ_HUGE || mon == u.ustuck || mon->mtrapped) 664 return; 665 666 /* Make sure dx and dy are [-1,0,1] */ 667 dx = sgn(dx); 668 dy = sgn(dy); 669 if(!range || (!dx && !dy)) return; /* paranoia */ 670 671 /* Send the monster along the path */ 672 mc.x = mon->mx; 673 mc.y = mon->my; 674 cc.x = mon->mx + (dx * range); 675 cc.y = mon->my + (dy * range); 676 (void) walk_path(&mc, &cc, mhurtle_step, (genericptr_t)mon); 677 return; 678} 679 680STATIC_OVL void 681check_shop_obj(obj, x, y, broken) 682register struct obj *obj; 683register xchar x, y; 684register boolean broken; 685{ 686 struct monst *shkp = shop_keeper(*u.ushops); 687 688 if(!shkp) return; 689 690 if(broken) { 691 if (obj->unpaid) { 692 (void)stolen_value(obj, u.ux, u.uy, 693 (boolean)shkp->mpeaceful, FALSE); 694 subfrombill(obj, shkp); 695 } 696 obj->no_charge = 1; 697 return; 698 } 699 700 if (!costly_spot(x, y) || *in_rooms(x, y, SHOPBASE) != *u.ushops) { 701 /* thrown out of a shop or into a different shop */ 702 if (obj->unpaid) { 703 (void)stolen_value(obj, u.ux, u.uy, 704 (boolean)shkp->mpeaceful, FALSE); 705 subfrombill(obj, shkp); 706 } 707 } else { 708 if (costly_spot(u.ux, u.uy) && costly_spot(x, y)) { 709 if(obj->unpaid) subfrombill(obj, shkp); 710 else if(!(x == shkp->mx && y == shkp->my)) 711 sellobj(obj, x, y); 712 } 713 } 714} 715 716/* 717 * Hero tosses an object upwards with appropriate consequences. 718 * 719 * Returns FALSE if the object is gone. 720 */ 721STATIC_OVL boolean 722toss_up(obj, hitsroof) 723struct obj *obj; 724boolean hitsroof; 725{ 726 const char *almost; 727 /* note: obj->quan == 1 */ 728 729 if (hitsroof) { 730 if (breaktest(obj)) { 731 pline("%s hits the %s.", Doname2(obj), ceiling(u.ux, u.uy)); 732 breakmsg(obj, !Blind); 733 breakobj(obj, u.ux, u.uy, TRUE, TRUE); 734 return FALSE; 735 } 736 almost = ""; 737 } else { 738 almost = " almost"; 739 } 740 pline("%s%s hits the %s, then falls back on top of your %s.", 741 Doname2(obj), almost, ceiling(u.ux,u.uy), body_part(HEAD)); 742 743 /* object now hits you */ 744 745 if (obj->oclass == POTION_CLASS) { 746 potionhit(&youmonst, obj, TRUE); 747 } else if (breaktest(obj)) { 748 int otyp = obj->otyp, ocorpsenm = obj->corpsenm; 749 int blindinc; 750 751 /* need to check for blindness result prior to destroying obj */ 752 blindinc = (otyp == CREAM_PIE || otyp == BLINDING_VENOM) && 753 /* AT_WEAP is ok here even if attack type was AT_SPIT */ 754 can_blnd(&youmonst, &youmonst, AT_WEAP, obj) ? rnd(25) : 0; 755 756 breakmsg(obj, !Blind); 757 breakobj(obj, u.ux, u.uy, TRUE, TRUE); 758 obj = 0; /* it's now gone */ 759 switch (otyp) { 760 case EGG: 761 if (touch_petrifies(&mons[ocorpsenm]) && 762 !uarmh && !Stone_resistance && 763 !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) 764 goto petrify; 765 case CREAM_PIE: 766 case BLINDING_VENOM: 767 pline("You've got it all over your %s!", body_part(FACE)); 768 if (blindinc) { 769 if (otyp == BLINDING_VENOM && !Blind) 770 pline("It blinds you!"); 771 u.ucreamed += blindinc; 772 make_blinded(Blinded + (long)blindinc, FALSE); 773 if (!Blind) Your(vision_clears); 774 } 775 break; 776 default: 777 break; 778 } 779 return FALSE; 780 } else { /* neither potion nor other breaking object */ 781 boolean less_damage = uarmh && is_metallic(uarmh), artimsg = FALSE; 782 int dmg = dmgval(obj, &youmonst); 783 784 if (obj->oartifact) 785 /* need a fake die roll here; rn1(18,2) avoids 1 and 20 */ 786 artimsg = artifact_hit((struct monst *)0, &youmonst, 787 obj, &dmg, rn1(18,2)); 788 789 if (!dmg) { /* probably wasn't a weapon; base damage on weight */ 790 dmg = (int) obj->owt / 100; 791 if (dmg < 1) dmg = 1; 792 else if (dmg > 6) dmg = 6; 793 if (youmonst.data == &mons[PM_SHADE] && 794 objects[obj->otyp].oc_material != SILVER) 795 dmg = 0; 796 } 797 if (dmg > 1 && less_damage) dmg = 1; 798 if (dmg > 0) dmg += u.udaminc; 799 if (dmg < 0) dmg = 0; /* beware negative rings of increase damage */ 800 if (Half_physical_damage) dmg = (dmg + 1) / 2; 801 802 if (uarmh) { 803 if (less_damage && dmg < (Upolyd ? u.mh : u.uhp)) { 804 if (!artimsg) 805 pline("Fortunately, you are wearing a hard helmet."); 806 } else if (flags.verbose && 807 !(obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm]))) 808 Your("%s does not protect you.", xname(uarmh)); 809 } else if (obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])) { 810 if (!Stone_resistance && 811 !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) { 812 petrify: 813 killer_format = KILLED_BY; 814 killer = "elementary physics"; /* "what goes up..." */ 815 You("turn to stone."); 816 if (obj) dropy(obj); /* bypass most of hitfloor() */ 817 done(STONING); 818 return obj ? TRUE : FALSE; 819 } 820 } 821 hitfloor(obj); 822 losehp(dmg, "falling object", KILLED_BY_AN); 823 } 824 return TRUE; 825} 826 827/* return true for weapon meant to be thrown; excludes ammo */ 828STATIC_OVL boolean 829throwing_weapon(obj) 830struct obj *obj; 831{ 832 return (is_missile(obj) || is_spear(obj) || 833 /* daggers and knife (excludes scalpel) */ 834 (is_blade(obj) && !is_sword(obj) && 835 (objects[obj->otyp].oc_dir & PIERCE)) || 836 /* special cases [might want to add AXE] */ 837 obj->otyp == WAR_HAMMER || obj->otyp == AKLYS); 838} 839 840/* the currently thrown object is returning to you (not for boomerangs) */ 841STATIC_OVL void 842sho_obj_return_to_u(obj) 843struct obj *obj; 844{ 845 /* might already be our location (bounced off a wall) */ 846 if (bhitpos.x != u.ux || bhitpos.y != u.uy) { 847 int x = bhitpos.x - u.dx, y = bhitpos.y - u.dy; 848 849 tmp_at(DISP_FLASH, obj_to_glyph(obj)); 850 while(x != u.ux || y != u.uy) { 851 tmp_at(x, y); 852 delay_output(); 853 x -= u.dx; y -= u.dy; 854 } 855 tmp_at(DISP_END, 0); 856 } 857} 858 859void 860throwit(obj, wep_mask, twoweap) 861register struct obj *obj; 862long wep_mask; /* used to re-equip returning boomerang */ 863boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */ 864{ 865 register struct monst *mon; 866 register int range, urange; 867 boolean impaired = (Confusion || Stunned || Blind || 868 Hallucination || Fumbling); 869 870 if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) { 871 boolean slipok = TRUE; 872 if (ammo_and_launcher(obj, uwep)) 873 pline("%s!", Tobjnam(obj, "misfire")); 874 else { 875 /* only slip if it's greased or meant to be thrown */ 876 if (obj->greased || throwing_weapon(obj)) 877 /* BUG: this message is grammatically incorrect if obj has 878 a plural name; greased gloves or boots for instance. */ 879 pline("%s as you throw it!", Tobjnam(obj, "slip")); 880 else slipok = FALSE; 881 } 882 if (slipok) { 883 u.dx = rn2(3)-1; 884 u.dy = rn2(3)-1; 885 if (!u.dx && !u.dy) u.dz = 1; 886 impaired = TRUE; 887 } 888 } 889 890 if ((u.dx || u.dy || (u.dz < 1)) && 891 calc_capacity((int)obj->owt) > SLT_ENCUMBER && 892 (Upolyd ? (u.mh < 5 && u.mh != u.mhmax) 893 : (u.uhp < 10 && u.uhp != u.uhpmax)) && 894 obj->owt > (unsigned)((Upolyd ? u.mh : u.uhp) * 2) && 895 !Is_airlevel(&u.uz)) { 896 You("have so little stamina, %s drops from your grasp.", 897 the(xname(obj))); 898 exercise(A_CON, FALSE); 899 u.dx = u.dy = 0; 900 u.dz = 1; 901 } 902 903 thrownobj = obj; 904 905 if(u.uswallow) { 906 mon = u.ustuck; 907 bhitpos.x = mon->mx; 908 bhitpos.y = mon->my; 909 } else if(u.dz) { 910 if (u.dz < 0 && Role_if(PM_VALKYRIE) && 911 obj->oartifact == ART_MJOLLNIR && !impaired) { 912 pline("%s the %s and returns to your hand!", 913 Tobjnam(obj, "hit"), ceiling(u.ux,u.uy)); 914 obj = addinv(obj); 915 (void) encumber_msg(); 916 setuwep(obj); 917 u.twoweap = twoweap; 918 } else if (u.dz < 0 && !Is_airlevel(&u.uz) && 919 !Underwater && !Is_waterlevel(&u.uz)) { 920 (void) toss_up(obj, rn2(5)); 921 } else { 922 hitfloor(obj); 923 } 924 thrownobj = (struct obj*)0; 925 return; 926 927 } else if(obj->otyp == BOOMERANG && !Underwater) { 928 if(Is_airlevel(&u.uz) || Levitation) 929 hurtle(-u.dx, -u.dy, 1, TRUE); 930 mon = boomhit(u.dx, u.dy); 931 if(mon == &youmonst) { /* the thing was caught */ 932 exercise(A_DEX, TRUE); 933 obj = addinv(obj); 934 (void) encumber_msg(); 935 if (wep_mask && !(obj->owornmask & wep_mask)) { 936 setworn(obj, wep_mask); 937 u.twoweap = twoweap; 938 } 939 thrownobj = (struct obj*)0; 940 return; 941 } 942 } else { 943 urange = (int)(ACURRSTR)/2; 944 /* balls are easy to throw or at least roll */ 945 /* also, this insures the maximum range of a ball is greater 946 * than 1, so the effects from throwing attached balls are 947 * actually possible 948 */ 949 if (obj->otyp == HEAVY_IRON_BALL) 950 range = urange - (int)(obj->owt/100); 951 else 952 range = urange - (int)(obj->owt/40); 953 if (obj == uball) { 954 if (u.ustuck) range = 1; 955 else if (range >= 5) range = 5; 956 } 957 if (range < 1) range = 1; 958 959 if (is_ammo(obj)) { 960 if (ammo_and_launcher(obj, uwep)) 961 range++; 962 else if (obj->oclass != GEM_CLASS) 963 range /= 2; 964 } 965 966 if (Is_airlevel(&u.uz) || Levitation) { 967 /* action, reaction... */ 968 urange -= range; 969 if(urange < 1) urange = 1; 970 range -= urange; 971 if(range < 1) range = 1; 972 } 973 974 if (obj->otyp == BOULDER) 975 range = 20; /* you must be giant */ 976 else if (obj->oartifact == ART_MJOLLNIR) 977 range = (range + 1) / 2; /* it's heavy */ 978 else if (obj == uball && u.utrap && u.utraptype == TT_INFLOOR) 979 range = 1; 980 981 if (Underwater) range = 1; 982 983 mon = bhit(u.dx, u.dy, range, THROWN_WEAPON, 984 (int FDECL((*),(MONST_P,OBJ_P)))0, 985 (int FDECL((*),(OBJ_P,OBJ_P)))0, 986 obj); 987 988 /* have to do this after bhit() so u.ux & u.uy are correct */ 989 if(Is_airlevel(&u.uz) || Levitation) 990 hurtle(-u.dx, -u.dy, urange, TRUE); 991 } 992 993 if (mon) { 994 boolean obj_gone; 995 996 if (mon->isshk && 997 obj->where == OBJ_MINVENT && obj->ocarry == mon) { 998 thrownobj = (struct obj*)0; 999 return; /* alert shk caught it */ 1000 } 1001 (void) snuff_candle(obj); 1002 notonhead = (bhitpos.x != mon->mx || bhitpos.y != mon->my); 1003 obj_gone = thitmonst(mon, obj); 1004 /* Monster may have been tamed; this frees old mon */ 1005 mon = m_at(bhitpos.x, bhitpos.y); 1006 1007 /* [perhaps this should be moved into thitmonst or hmon] */ 1008 if (mon && mon->isshk && 1009 (!inside_shop(u.ux, u.uy) || 1010 !index(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops))) 1011 hot_pursuit(mon); 1012 1013 if (obj_gone) return; 1014 } 1015 1016 if (u.uswallow) { 1017 /* ball is not picked up by monster */ 1018 if (obj != uball) (void) mpickobj(u.ustuck,obj); 1019 } else { 1020 /* the code following might become part of dropy() */ 1021 if (obj->oartifact == ART_MJOLLNIR && 1022 Role_if(PM_VALKYRIE) && rn2(100)) { 1023 /* we must be wearing Gauntlets of Power to get here */ 1024 sho_obj_return_to_u(obj); /* display its flight */ 1025 1026 if (!impaired && rn2(100)) { 1027 pline("%s to your hand!", Tobjnam(obj, "return")); 1028 obj = addinv(obj); 1029 (void) encumber_msg(); 1030 setuwep(obj); 1031 u.twoweap = twoweap; 1032 if(cansee(bhitpos.x, bhitpos.y)) 1033 newsym(bhitpos.x,bhitpos.y); 1034 } else { 1035 int dmg = rn2(2); 1036 if (!dmg) { 1037 pline(Blind ? "%s lands %s your %s." : 1038 "%s back to you, landing %s your %s.", 1039 Blind ? Something : Tobjnam(obj, "return"), 1040 Levitation ? "beneath" : "at", 1041 makeplural(body_part(FOOT))); 1042 } else { 1043 dmg += rnd(3); 1044 pline(Blind ? "%s your %s!" : 1045 "%s back toward you, hitting your %s!", 1046 Tobjnam(obj, Blind ? "hit" : "fly"), 1047 body_part(ARM)); 1048 (void) artifact_hit((struct monst *)0, 1049 &youmonst, obj, &dmg, 0); 1050 losehp(dmg, xname(obj), 1051 obj_is_pname(obj) ? KILLED_BY : KILLED_BY_AN); 1052 } 1053 if (ship_object(obj, u.ux, u.uy, FALSE)) { 1054 thrownobj = (struct obj*)0; 1055 return; 1056 } 1057 dropy(obj); 1058 } 1059 thrownobj = (struct obj*)0; 1060 return; 1061 } 1062 1063 if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ) && 1064 breaktest(obj)) { 1065 tmp_at(DISP_FLASH, obj_to_glyph(obj)); 1066 tmp_at(bhitpos.x, bhitpos.y); 1067 delay_output(); 1068 tmp_at(DISP_END, 0); 1069 breakmsg(obj, cansee(bhitpos.x, bhitpos.y)); 1070 breakobj(obj, bhitpos.x, bhitpos.y, TRUE, TRUE); 1071 return; 1072 } 1073 if(flooreffects(obj,bhitpos.x,bhitpos.y,"fall")) return; 1074 obj_no_longer_held(obj); 1075 if (mon && mon->isshk && is_pick(obj)) { 1076 if (cansee(bhitpos.x, bhitpos.y)) 1077 pline("%s snatches up %s.", 1078 Monnam(mon), the(xname(obj))); 1079 if(*u.ushops) 1080 check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE); 1081 (void) mpickobj(mon, obj); /* may merge and free obj */ 1082 thrownobj = (struct obj*)0; 1083 return; 1084 } 1085 (void) snuff_candle(obj); 1086 if (!mon && ship_object(obj, bhitpos.x, bhitpos.y, FALSE)) { 1087 thrownobj = (struct obj*)0; 1088 return; 1089 } 1090 thrownobj = (struct obj*)0; 1091 place_object(obj, bhitpos.x, bhitpos.y); 1092 if(*u.ushops && obj != uball) 1093 check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE); 1094 1095 stackobj(obj); 1096 if (obj == uball) 1097 drop_ball(bhitpos.x, bhitpos.y); 1098 if (cansee(bhitpos.x, bhitpos.y)) 1099 newsym(bhitpos.x,bhitpos.y); 1100 if (obj_sheds_light(obj)) 1101 vision_full_recalc = 1; 1102 if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ)) 1103 container_impact_dmg(obj); 1104 } 1105} 1106 1107/* an object may hit a monster; various factors adjust the chance of hitting */ 1108int 1109omon_adj(mon, obj, mon_notices) 1110struct monst *mon; 1111struct obj *obj; 1112boolean mon_notices; 1113{ 1114 int tmp = 0; 1115 1116 /* size of target affects the chance of hitting */ 1117 tmp += (mon->data->msize - MZ_MEDIUM); /* -2..+5 */ 1118 /* sleeping target is more likely to be hit */ 1119 if (mon->msleeping) { 1120 tmp += 2; 1121 if (mon_notices) mon->msleeping = 0; 1122 } 1123 /* ditto for immobilized target */ 1124 if (!mon->mcanmove || !mon->data->mmove) { 1125 tmp += 4; 1126 if (mon_notices && mon->data->mmove && !rn2(10)) { 1127 mon->mcanmove = 1; 1128 mon->mfrozen = 0; 1129 } 1130 } 1131 /* some objects are more likely to hit than others */ 1132 switch (obj->otyp) { 1133 case HEAVY_IRON_BALL: 1134 if (obj != uball) tmp += 2; 1135 break; 1136 case BOULDER: 1137 tmp += 6; 1138 break; 1139 default: 1140 if (obj->oclass == WEAPON_CLASS || is_weptool(obj) || 1141 obj->oclass == GEM_CLASS) 1142 tmp += hitval(obj, mon); 1143 break; 1144 } 1145 return tmp; 1146} 1147 1148/* thrown object misses target monster */ 1149STATIC_OVL void 1150tmiss(obj, mon) 1151struct obj *obj; 1152struct monst *mon; 1153{ 1154 const char *missile = mshot_xname(obj); 1155 1156 /* If the target can't be seen or doesn't look like a valid target, 1157 avoid "the arrow misses it," or worse, "the arrows misses the mimic." 1158 An attentive player will still notice that this is different from 1159 an arrow just landing short of any target (no message in that case), 1160 so will realize that there is a valid target here anyway. */ 1161 if (!canseemon(mon) || (mon->m_ap_type && mon->m_ap_type != M_AP_MONSTER)) 1162 pline("%s %s.", The(missile), otense(obj, "miss")); 1163 else 1164 miss(missile, mon); 1165 if (!rn2(3)) wakeup(mon); 1166 return; 1167} 1168 1169#define quest_arti_hits_leader(obj,mon) \ 1170 (obj->oartifact && is_quest_artifact(obj) && (mon->data->msound == MS_LEADER)) 1171 1172/* 1173 * Object thrown by player arrives at monster's location. 1174 * Return 1 if obj has disappeared or otherwise been taken care of, 1175 * 0 if caller must take care of it. 1176 */ 1177int 1178thitmonst(mon, obj) 1179register struct monst *mon; 1180register struct obj *obj; 1181{ 1182 register int tmp; /* Base chance to hit */ 1183 register int disttmp; /* distance modifier */ 1184 int otyp = obj->otyp; 1185 boolean guaranteed_hit = (u.uswallow && mon == u.ustuck); 1186 1187 /* Differences from melee weapons: 1188 * 1189 * Dex still gives a bonus, but strength does not. 1190 * Polymorphed players lacking attacks may still throw. 1191 * There's a base -1 to hit. 1192 * No bonuses for fleeing or stunned targets (they don't dodge 1193 * melee blows as readily, but dodging arrows is hard anyway). 1194 * Not affected by traps, etc. 1195 * Certain items which don't in themselves do damage ignore tmp. 1196 * Distance and monster size affect chance to hit. 1197 */ 1198 tmp = -1 + Luck + find_mac(mon) + u.uhitinc + 1199 maybe_polyd(youmonst.data->mlevel, u.ulevel); 1200 if (ACURR(A_DEX) < 4) tmp -= 3; 1201 else if (ACURR(A_DEX) < 6) tmp -= 2; 1202 else if (ACURR(A_DEX) < 8) tmp -= 1; 1203 else if (ACURR(A_DEX) >= 14) tmp += (ACURR(A_DEX) - 14); 1204 1205 /* Modify to-hit depending on distance; but keep it sane. 1206 * Polearms get a distance penalty even when wielded; it's 1207 * hard to hit at a distance. 1208 */ 1209 disttmp = 3 - distmin(u.ux, u.uy, mon->mx, mon->my); 1210 if(disttmp < -4) disttmp = -4; 1211 tmp += disttmp; 1212 1213 /* gloves are a hinderance to proper use of bows */ 1214 if (uarmg && uwep && objects[uwep->otyp].oc_skill == P_BOW) { 1215 switch (uarmg->otyp) { 1216 case GAUNTLETS_OF_POWER: /* metal */ 1217 tmp -= 2; 1218 break; 1219 case GAUNTLETS_OF_FUMBLING: 1220 tmp -= 3; 1221 break; 1222 case LEATHER_GLOVES: 1223 case GAUNTLETS_OF_DEXTERITY: 1224 break; 1225 default: 1226 impossible("Unknown type of gloves (%d)", uarmg->otyp); 1227 break; 1228 } 1229 } 1230 1231 tmp += omon_adj(mon, obj, TRUE); 1232 if (is_orc(mon->data) && maybe_polyd(is_elf(youmonst.data), 1233 Race_if(PM_ELF))) 1234 tmp++; 1235 if (guaranteed_hit) { 1236 tmp += 1000; /* Guaranteed hit */ 1237 } 1238 1239 if (obj->oclass == GEM_CLASS && is_unicorn(mon->data)) { 1240 if (mon->mtame) { 1241 pline("%s catches and drops %s.", Monnam(mon), the(xname(obj))); 1242 return 0; 1243 } else { 1244 pline("%s catches %s.", Monnam(mon), the(xname(obj))); 1245 return gem_accept(mon, obj); 1246 } 1247 } 1248 1249 /* don't make game unwinnable if naive player throws artifact 1250 at leader.... */ 1251 if (quest_arti_hits_leader(obj, mon)) { 1252 /* not wakeup(), which angers non-tame monsters */ 1253 mon->msleeping = 0; 1254 mon->mstrategy &= ~STRAT_WAITMASK; 1255 1256 if (mon->mcanmove) { 1257 pline("%s catches %s.", Monnam(mon), the(xname(obj))); 1258 if (mon->mpeaceful) { 1259 boolean next2u = monnear(mon, u.ux, u.uy); 1260 1261 finish_quest(obj); /* acknowledge quest completion */ 1262 pline("%s %s %s back to you.", Monnam(mon), 1263 (next2u ? "hands" : "tosses"), the(xname(obj))); 1264 if (!next2u) sho_obj_return_to_u(obj); 1265 obj = addinv(obj); /* back into your inventory */ 1266 (void) encumber_msg(); 1267 } else { 1268 /* angry leader caught it and isn't returning it */ 1269 (void) mpickobj(mon, obj); 1270 } 1271 return 1; /* caller doesn't need to place it */ 1272 } 1273 return(0); 1274 } 1275 1276 if (obj->oclass == WEAPON_CLASS || is_weptool(obj) || 1277 obj->oclass == GEM_CLASS) { 1278 if (is_ammo(obj)) { 1279 if (!ammo_and_launcher(obj, uwep)) { 1280 tmp -= 4; 1281 } else { 1282 tmp += uwep->spe - greatest_erosion(uwep); 1283 tmp += weapon_hit_bonus(uwep); 1284 if (uwep->oartifact) tmp += spec_abon(uwep, mon); 1285 /* 1286 * Elves and Samurais are highly trained w/bows, 1287 * especially their own special types of bow. 1288 * Polymorphing won't make you a bow expert. 1289 */ 1290 if ((Race_if(PM_ELF) || Role_if(PM_SAMURAI)) && 1291 (!Upolyd || your_race(youmonst.data)) && 1292 objects[uwep->otyp].oc_skill == P_BOW) { 1293 tmp++; 1294 if (Race_if(PM_ELF) && uwep->otyp == ELVEN_BOW) 1295 tmp++; 1296 else if (Role_if(PM_SAMURAI) && uwep->otyp == YUMI) 1297 tmp++; 1298 } 1299 } 1300 } else { 1301 if (otyp == BOOMERANG) /* arbitrary */ 1302 tmp += 4; 1303 else if (throwing_weapon(obj)) /* meant to be thrown */ 1304 tmp += 2; 1305 else /* not meant to be thrown */ 1306 tmp -= 2; 1307 /* we know we're dealing with a weapon or weptool handled 1308 by WEAPON_SKILLS once ammo objects have been excluded */ 1309 tmp += weapon_hit_bonus(obj); 1310 } 1311 1312 if (tmp >= rnd(20)) { 1313 if (hmon(mon,obj,1)) { /* mon still alive */ 1314 cutworm(mon, bhitpos.x, bhitpos.y, obj); 1315 } 1316 exercise(A_DEX, TRUE); 1317 /* projectiles other than magic stones 1318 sometimes disappear when thrown */ 1319 if (objects[otyp].oc_skill < P_NONE && 1320 objects[otyp].oc_skill > -P_BOOMERANG && 1321 !objects[otyp].oc_magic) { 1322 /* we were breaking 2/3 of everything unconditionally. 1323 * we still don't want anything to survive unconditionally, 1324 * but we need ammo to stay around longer on average. 1325 */ 1326 int broken, chance; 1327 chance = 3 + greatest_erosion(obj) - obj->spe; 1328 if (chance > 1) 1329 broken = rn2(chance); 1330 else 1331 broken = !rn2(4); 1332 if (obj->blessed && !rnl(4)) 1333 broken = 0; 1334 1335 if (broken) { 1336 if (*u.ushops) 1337 check_shop_obj(obj, bhitpos.x,bhitpos.y, TRUE); 1338 obfree(obj, (struct obj *)0); 1339 return 1; 1340 } 1341 } 1342 passive_obj(mon, obj, (struct attack *)0); 1343 } else { 1344 tmiss(obj, mon); 1345 } 1346 1347 } else if (otyp == HEAVY_IRON_BALL) { 1348 exercise(A_STR, TRUE); 1349 if (tmp >= rnd(20)) { 1350 int was_swallowed = guaranteed_hit; 1351 1352 exercise(A_DEX, TRUE); 1353 if (!hmon(mon,obj,1)) { /* mon killed */ 1354 if (was_swallowed && !u.uswallow && obj == uball) 1355 return 1; /* already did placebc() */ 1356 } 1357 } else { 1358 tmiss(obj, mon); 1359 } 1360 1361 } else if (otyp == BOULDER) { 1362 exercise(A_STR, TRUE); 1363 if (tmp >= rnd(20)) { 1364 exercise(A_DEX, TRUE); 1365 (void) hmon(mon,obj,1); 1366 } else { 1367 tmiss(obj, mon); 1368 } 1369 1370 } else if ((otyp == EGG || otyp == CREAM_PIE || 1371 otyp == BLINDING_VENOM || otyp == ACID_VENOM) && 1372 (guaranteed_hit || ACURR(A_DEX) > rnd(25))) { 1373 (void) hmon(mon, obj, 1); 1374 return 1; /* hmon used it up */ 1375 1376 } else if (obj->oclass == POTION_CLASS && 1377 (guaranteed_hit || ACURR(A_DEX) > rnd(25))) { 1378 potionhit(mon, obj, TRUE); 1379 return 1; 1380 1381 } else if (befriend_with_obj(mon->data, obj) || 1382 (mon->mtame && dogfood(mon, obj) <= ACCFOOD)) { 1383 if (tamedog(mon, obj)) 1384 return 1; /* obj is gone */ 1385 else { 1386 /* not tmiss(), which angers non-tame monsters */ 1387 miss(xname(obj), mon); 1388 mon->msleeping = 0; 1389 mon->mstrategy &= ~STRAT_WAITMASK; 1390 } 1391 } else if (guaranteed_hit) { 1392 /* this assumes that guaranteed_hit is due to swallowing */ 1393 wakeup(mon); 1394 if (obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])) { 1395 if (is_animal(u.ustuck->data)) { 1396 minstapetrify(u.ustuck, TRUE); 1397 /* Don't leave a cockatrice corpse available in a statue */ 1398 if (!u.uswallow) { 1399 delobj(obj); 1400 return 1; 1401 } 1402 } 1403 } 1404 pline("%s into %s %s.", 1405 Tobjnam(obj, "vanish"), s_suffix(mon_nam(mon)), 1406 is_animal(u.ustuck->data) ? "entrails" : "currents"); 1407 } else { 1408 tmiss(obj, mon); 1409 } 1410 1411 return 0; 1412} 1413 1414STATIC_OVL int 1415gem_accept(mon, obj) 1416register struct monst *mon; 1417register struct obj *obj; 1418{ 1419 char buf[BUFSZ]; 1420 boolean is_buddy = sgn(mon->data->maligntyp) == sgn(u.ualign.type); 1421 boolean is_gem = objects[obj->otyp].oc_material == GEMSTONE; 1422 int ret = 0; 1423 static NEARDATA const char nogood[] = " is not interested in your junk."; 1424 static NEARDATA const char acceptgift[] = " accepts your gift."; 1425 static NEARDATA const char maybeluck[] = " hesitatingly"; 1426 static NEARDATA const char noluck[] = " graciously"; 1427 static NEARDATA const char addluck[] = " gratefully"; 1428 1429 Strcpy(buf,Monnam(mon)); 1430 mon->mpeaceful = 1; 1431 mon->mavenge = 0; 1432 1433 /* object properly identified */ 1434 if(obj->dknown && objects[obj->otyp].oc_name_known) { 1435 if(is_gem) { 1436 if(is_buddy) { 1437 Strcat(buf,addluck); 1438 change_luck(5); 1439 } else { 1440 Strcat(buf,maybeluck); 1441 change_luck(rn2(7)-3); 1442 } 1443 } else { 1444 Strcat(buf,nogood); 1445 goto nopick; 1446 } 1447 /* making guesses */ 1448 } else if(obj->onamelth || objects[obj->otyp].oc_uname) { 1449 if(is_gem) { 1450 if(is_buddy) { 1451 Strcat(buf,addluck); 1452 change_luck(2); 1453 } else { 1454 Strcat(buf,maybeluck); 1455 change_luck(rn2(3)-1); 1456 } 1457 } else { 1458 Strcat(buf,nogood); 1459 goto nopick; 1460 } 1461 /* value completely unknown to @ */ 1462 } else { 1463 if(is_gem) { 1464 if(is_buddy) { 1465 Strcat(buf,addluck); 1466 change_luck(1); 1467 } else { 1468 Strcat(buf,maybeluck); 1469 change_luck(rn2(3)-1); 1470 } 1471 } else { 1472 Strcat(buf,noluck); 1473 } 1474 } 1475 Strcat(buf,acceptgift); 1476 if(*u.ushops) check_shop_obj(obj, mon->mx, mon->my, TRUE); 1477 (void) mpickobj(mon, obj); /* may merge and free obj */ 1478 ret = 1; 1479 1480nopick: 1481 if(!Blind) pline("%s", buf); 1482 if (!tele_restrict(mon)) (void) rloc(mon, FALSE); 1483 return(ret); 1484} 1485 1486/* 1487 * Comments about the restructuring of the old breaks() routine. 1488 * 1489 * There are now three distinct phases to object breaking: 1490 * breaktest() - which makes the check/decision about whether the 1491 * object is going to break. 1492 * breakmsg() - which outputs a message about the breakage, 1493 * appropriate for that particular object. Should 1494 * only be called after a positve breaktest(). 1495 * on the object and, if it going to be called, 1496 * it must be called before calling breakobj(). 1497 * Calling breakmsg() is optional. 1498 * breakobj() - which actually does the breakage and the side-effects 1499 * of breaking that particular object. This should 1500 * only be called after a positive breaktest() on the 1501 * object. 1502 * 1503 * Each of the above routines is currently static to this source module. 1504 * There are two routines callable from outside this source module which 1505 * perform the routines above in the correct sequence. 1506 * 1507 * hero_breaks() - called when an object is to be broken as a result 1508 * of something that the hero has done. (throwing it, 1509 * kicking it, etc.) 1510 * breaks() - called when an object is to be broken for some 1511 * reason other than the hero doing something to it. 1512 */ 1513 1514/* 1515 * The hero causes breakage of an object (throwing, dropping it, etc.) 1516 * Return 0 if the object didn't break, 1 if the object broke. 1517 */ 1518int 1519hero_breaks(obj, x, y, from_invent) 1520struct obj *obj; 1521xchar x, y; /* object location (ox, oy may not be right) */ 1522boolean from_invent; /* thrown or dropped by player; maybe on shop bill */ 1523{ 1524 boolean in_view = !Blind; 1525 if (!breaktest(obj)) return 0; 1526 breakmsg(obj, in_view); 1527 breakobj(obj, x, y, TRUE, from_invent); 1528 return 1; 1529} 1530 1531/* 1532 * The object is going to break for a reason other than the hero doing 1533 * something to it. 1534 * Return 0 if the object doesn't break, 1 if the object broke. 1535 */ 1536int 1537breaks(obj, x, y) 1538struct obj *obj; 1539xchar x, y; /* object location (ox, oy may not be right) */ 1540{ 1541 boolean in_view = Blind ? FALSE : cansee(x, y); 1542 1543 if (!breaktest(obj)) return 0; 1544 breakmsg(obj, in_view); 1545 breakobj(obj, x, y, FALSE, FALSE); 1546 return 1; 1547} 1548 1549/* 1550 * Unconditionally break an object. Assumes all resistance checks 1551 * and break messages have been delivered prior to getting here. 1552 */ 1553STATIC_OVL void 1554breakobj(obj, x, y, hero_caused, from_invent) 1555struct obj *obj; 1556xchar x, y; /* object location (ox, oy may not be right) */ 1557boolean hero_caused; /* is this the hero's fault? */ 1558boolean from_invent; 1559{ 1560 switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { 1561 case MIRROR: 1562 if (hero_caused) 1563 change_luck(-2); 1564 break; 1565 case POT_WATER: /* really, all potions */ 1566 if (obj->otyp == POT_OIL && obj->lamplit) { 1567 splatter_burning_oil(x,y); 1568 } else if (distu(x,y) <= 2) { 1569 if (!breathless(youmonst.data) || haseyes(youmonst.data)) { 1570 if (obj->otyp != POT_WATER) { 1571 if (!breathless(youmonst.data)) 1572 /* [what about "familiar odor" when known?] */ 1573 You("smell a peculiar odor..."); 1574 else { 1575 int numeyes = eyecount(youmonst.data); 1576 Your("%s water%s.", 1577 (numeyes == 1) ? body_part(EYE) : 1578 makeplural(body_part(EYE)), 1579 (numeyes == 1) ? "s" : ""); 1580 } 1581 } 1582 potionbreathe(obj); 1583 } 1584 } 1585 /* monster breathing isn't handled... [yet?] */ 1586 break; 1587 case EGG: 1588 /* breaking your own eggs is bad luck */ 1589 if (hero_caused && obj->spe && obj->corpsenm >= LOW_PM) 1590 change_luck((schar) -min(obj->quan, 5L)); 1591 break; 1592 } 1593 if (hero_caused) { 1594 if (from_invent) { 1595 if (*u.ushops) 1596 check_shop_obj(obj, x, y, TRUE); 1597 } else if (!obj->no_charge && costly_spot(x, y)) { 1598 /* it is assumed that the obj is a floor-object */ 1599 char *o_shop = in_rooms(x, y, SHOPBASE); 1600 struct monst *shkp = shop_keeper(*o_shop); 1601 1602 if (shkp) { /* (implies *o_shop != '\0') */ 1603 static NEARDATA long lastmovetime = 0L; 1604 static NEARDATA boolean peaceful_shk = FALSE; 1605 /* We want to base shk actions on her peacefulness 1606 at start of this turn, so that "simultaneous" 1607 multiple breakage isn't drastically worse than 1608 single breakage. (ought to be done via ESHK) */ 1609 if (moves != lastmovetime) 1610 peaceful_shk = shkp->mpeaceful; 1611 if (stolen_value(obj, x, y, peaceful_shk, FALSE) > 0L && 1612 (*o_shop != u.ushops[0] || !inside_shop(u.ux, u.uy)) && 1613 moves != lastmovetime) make_angry_shk(shkp, x, y); 1614 lastmovetime = moves; 1615 } 1616 } 1617 } 1618 delobj(obj); 1619} 1620 1621/* 1622 * Check to see if obj is going to break, but don't actually break it. 1623 * Return 0 if the object isn't going to break, 1 if it is. 1624 */ 1625boolean 1626breaktest(obj) 1627struct obj *obj; 1628{ 1629 if (obj_resists(obj, 1, 99)) return 0; 1630 if (objects[obj->otyp].oc_material == GLASS && !obj->oartifact && 1631 obj->oclass != GEM_CLASS) 1632 return 1; 1633 switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { 1634#ifdef TOURIST 1635 case EXPENSIVE_CAMERA: 1636#endif 1637 case POT_WATER: /* really, all potions */ 1638 case EGG: 1639 case CREAM_PIE: 1640 case MELON: 1641 case ACID_VENOM: 1642 case BLINDING_VENOM: 1643 return 1; 1644 default: 1645 return 0; 1646 } 1647} 1648 1649STATIC_OVL void 1650breakmsg(obj, in_view) 1651struct obj *obj; 1652boolean in_view; 1653{ 1654 const char *to_pieces; 1655 1656 to_pieces = ""; 1657 switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { 1658 default: /* glass or crystal wand */ 1659 if (obj->oclass != WAND_CLASS) 1660 impossible("breaking odd object?"); 1661 case CRYSTAL_PLATE_MAIL: 1662 case LENSES: 1663 case MIRROR: 1664 case CRYSTAL_BALL: 1665#ifdef TOURIST 1666 case EXPENSIVE_CAMERA: 1667#endif 1668 to_pieces = " into a thousand pieces"; 1669 /*FALLTHRU*/ 1670 case POT_WATER: /* really, all potions */ 1671 if (!in_view) 1672 You_hear("%s shatter!", something); 1673 else 1674 pline("%s shatter%s%s!", Doname2(obj), 1675 (obj->quan==1) ? "s" : "", to_pieces); 1676 break; 1677 case EGG: 1678 case MELON: 1679 pline("Splat!"); 1680 break; 1681 case CREAM_PIE: 1682 if (in_view) pline("What a mess!"); 1683 break; 1684 case ACID_VENOM: 1685 case BLINDING_VENOM: 1686 pline("Splash!"); 1687 break; 1688 } 1689} 1690 1691STATIC_OVL int 1692throw_gold(obj) 1693struct obj *obj; 1694{ 1695 int range, odx, ody; 1696#ifndef GOLDOBJ 1697 long zorks = obj->quan; 1698#endif 1699 register struct monst *mon; 1700 1701 if(!u.dx && !u.dy && !u.dz) { 1702#ifndef GOLDOBJ 1703 u.ugold += obj->quan; 1704 flags.botl = 1; 1705 dealloc_obj(obj); 1706#endif 1707 You("cannot throw gold at yourself."); 1708 return(0); 1709 } 1710#ifdef GOLDOBJ 1711 freeinv(obj); 1712#endif 1713 if(u.uswallow) { 1714 pline(is_animal(u.ustuck->data) ? 1715 "%s in the %s's entrails." : "%s into %s.", 1716#ifndef GOLDOBJ 1717 "The gold disappears", mon_nam(u.ustuck)); 1718 u.ustuck->mgold += zorks; 1719 dealloc_obj(obj); 1720#else 1721 "The money disappears", mon_nam(u.ustuck)); 1722 add_to_minv(u.ustuck, obj); 1723#endif 1724 return(1); 1725 } 1726 1727 if(u.dz) { 1728 if (u.dz < 0 && !Is_airlevel(&u.uz) && 1729 !Underwater && !Is_waterlevel(&u.uz)) { 1730 pline_The("gold hits the %s, then falls back on top of your %s.", 1731 ceiling(u.ux,u.uy), body_part(HEAD)); 1732 /* some self damage? */ 1733 if(uarmh) pline("Fortunately, you are wearing a helmet!"); 1734 } 1735 bhitpos.x = u.ux; 1736 bhitpos.y = u.uy; 1737 } else { 1738 /* consistent with range for normal objects */ 1739 range = (int)((ACURRSTR)/2 - obj->owt/40); 1740 1741 /* see if the gold has a place to move into */ 1742 odx = u.ux + u.dx; 1743 ody = u.uy + u.dy; 1744 if(!ZAP_POS(levl[odx][ody].typ) || closed_door(odx, ody)) { 1745 bhitpos.x = u.ux; 1746 bhitpos.y = u.uy; 1747 } else { 1748 mon = bhit(u.dx, u.dy, range, THROWN_WEAPON, 1749 (int FDECL((*),(MONST_P,OBJ_P)))0, 1750 (int FDECL((*),(OBJ_P,OBJ_P)))0, 1751 obj); 1752 if(mon) { 1753 if (ghitm(mon, obj)) /* was it caught? */ 1754 return 1; 1755 } else { 1756 if(ship_object(obj, bhitpos.x, bhitpos.y, FALSE)) 1757 return 1; 1758 } 1759 } 1760 } 1761 1762 if(flooreffects(obj,bhitpos.x,bhitpos.y,"fall")) return(1); 1763 if(u.dz > 0) 1764 pline_The("gold hits the %s.", surface(bhitpos.x,bhitpos.y)); 1765 place_object(obj,bhitpos.x,bhitpos.y); 1766 if(*u.ushops) sellobj(obj, bhitpos.x, bhitpos.y); 1767 stackobj(obj); 1768 newsym(bhitpos.x,bhitpos.y); 1769 return(1); 1770} 1771 1772/*dothrow.c*/ 1773