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
throw_obj(obj,shotlimit)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
dothrow()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
autoquiver()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
dofire()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
hitfloor(obj)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
walk_path(src_cc,dest_cc,check_proc,arg)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
hurtle_step(arg,x,y)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
hurtle(dx,dy,range,verbose)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
check_shop_obj(obj,x,y,broken)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
toss_up(obj,hitsroof)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
throwing_weapon(obj)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
sho_obj_return_to_u(obj)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
throwit(obj,wep_mask)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
omon_adj(mon,obj,mon_notices)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
tmiss(obj,mon)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
thitmonst(mon,obj)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
gem_accept(mon,obj)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
hero_breaks(obj,x,y,from_invent)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
breaks(obj,x,y)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
breakobj(obj,x,y,heros_fault,from_invent)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
breaktest(obj)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
breakmsg(obj,in_view)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
throw_gold(obj)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*/
1472