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