1 /* NetHack 3.6 dothrow.c $NHDT-Date: 1573688688 2019/11/13 23:44:48 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.164 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2013. */
4 /* NetHack may be freely redistributed. See license for details. */
5
6 /* Contains code for 't' (throw) */
7
8 #include "hack.h"
9
10 STATIC_DCL int FDECL(throw_obj, (struct obj *, int));
11 STATIC_DCL boolean FDECL(ok_to_throw, (int *));
12 STATIC_DCL void NDECL(autoquiver);
13 STATIC_DCL int FDECL(gem_accept, (struct monst *, struct obj *));
14 STATIC_DCL void FDECL(tmiss, (struct obj *, struct monst *, BOOLEAN_P));
15 STATIC_DCL int FDECL(throw_gold, (struct obj *));
16 STATIC_DCL void FDECL(check_shop_obj, (struct obj *, XCHAR_P, XCHAR_P,
17 BOOLEAN_P));
18 STATIC_DCL void FDECL(breakmsg, (struct obj *, BOOLEAN_P));
19 STATIC_DCL boolean FDECL(toss_up, (struct obj *, BOOLEAN_P));
20 STATIC_DCL void FDECL(sho_obj_return_to_u, (struct obj * obj));
21 STATIC_DCL boolean FDECL(mhurtle_step, (genericptr_t, int, int));
22
23 static NEARDATA const char toss_objs[] = { ALLOW_COUNT, COIN_CLASS,
24 ALL_CLASSES, WEAPON_CLASS, 0 };
25 /* different default choices when wielding a sling (gold must be included) */
26 static NEARDATA const char bullets[] = { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES,
27 GEM_CLASS, 0 };
28
29 /* thrownobj (decl.c) tracks an object until it lands */
30
31 extern boolean notonhead; /* for long worms */
32
33 /* Throw the selected object, asking for direction */
34 STATIC_OVL int
throw_obj(obj,shotlimit)35 throw_obj(obj, shotlimit)
36 struct obj *obj;
37 int shotlimit;
38 {
39 struct obj *otmp;
40 int multishot;
41 schar skill;
42 long wep_mask;
43 boolean twoweap, weakmultishot;
44
45 /* ask "in what direction?" */
46 if (!getdir((char *) 0)) {
47 /* No direction specified, so cancel the throw;
48 * might need to undo an object split.
49 * We used to use freeinv(obj),addinv(obj) here, but that can
50 * merge obj into another stack--usually quiver--even if it hadn't
51 * been split from there (possibly triggering a panic in addinv),
52 * and freeinv+addinv potentially has other side-effects.
53 */
54 if (obj->o_id == context.objsplit.parent_oid
55 || obj->o_id == context.objsplit.child_oid)
56 (void) unsplitobj(obj);
57 return 0; /* no time passes */
58 }
59
60 /*
61 * Throwing money is usually for getting rid of it when
62 * a leprechaun approaches, or for bribing an oncoming
63 * angry monster. So throw the whole object.
64 *
65 * If the money is in quiver, throw one coin at a time,
66 * possibly using a sling.
67 */
68 if (obj->oclass == COIN_CLASS && obj != uquiver)
69 return throw_gold(obj);
70
71 if (!canletgo(obj, "throw"))
72 return 0;
73 if (obj->oartifact == ART_MJOLLNIR && obj != uwep) {
74 pline("%s must be wielded before it can be thrown.", The(xname(obj)));
75 return 0;
76 }
77 if ((obj->oartifact == ART_MJOLLNIR && ACURR(A_STR) < STR19(25))
78 || (obj->otyp == BOULDER && !throws_rocks(youmonst.data))) {
79 pline("It's too heavy.");
80 return 1;
81 }
82 if (!u.dx && !u.dy && !u.dz) {
83 You("cannot throw an object at yourself.");
84 return 0;
85 }
86 u_wipe_engr(2);
87 if (!uarmg && obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])
88 && !Stone_resistance) {
89 You("throw %s with your bare %s.",
90 corpse_xname(obj, (const char *) 0, CXN_PFX_THE),
91 /* throwing with one hand, but pluralize since the
92 expression "with your bare hands" sounds better */
93 makeplural(body_part(HAND)));
94 Sprintf(killer.name, "throwing %s bare-handed", killer_xname(obj));
95 instapetrify(killer.name);
96 }
97 if (welded(obj)) {
98 weldmsg(obj);
99 return 1;
100 }
101 if (is_wet_towel(obj))
102 dry_a_towel(obj, -1, FALSE);
103
104 /* Multishot calculations
105 * (potential volley of up to N missiles; default for N is 1)
106 */
107 multishot = 1;
108 skill = objects[obj->otyp].oc_skill;
109 if (obj->quan > 1L /* no point checking if there's only 1 */
110 /* ammo requires corresponding launcher be wielded */
111 && (is_ammo(obj) ? matching_launcher(obj, uwep)
112 /* otherwise any stackable (non-ammo) weapon */
113 : obj->oclass == WEAPON_CLASS)
114 && !(Confusion || Stunned)) {
115 /* some roles don't get a volley bonus until becoming expert */
116 weakmultishot = (Role_if(PM_WIZARD) || Role_if(PM_PRIEST)
117 || (Role_if(PM_HEALER) && skill != P_KNIFE)
118 || (Role_if(PM_TOURIST) && skill != -P_DART)
119 /* poor dexterity also inhibits multishot */
120 || Fumbling || ACURR(A_DEX) <= 6);
121
122 /* Bonus if the player is proficient in this weapon... */
123 switch (P_SKILL(weapon_type(obj))) {
124 case P_EXPERT:
125 multishot++;
126 /*FALLTHRU*/
127 case P_SKILLED:
128 if (!weakmultishot)
129 multishot++;
130 break;
131 default: /* basic or unskilled: no bonus */
132 break;
133 }
134 /* ...or is using a special weapon for their role... */
135 switch (Role_switch) {
136 case PM_CAVEMAN:
137 /* give bonus for low-tech gear */
138 if (skill == -P_SLING || skill == P_SPEAR)
139 multishot++;
140 break;
141 case PM_MONK:
142 /* allow higher volley count despite skill limitation */
143 if (skill == -P_SHURIKEN)
144 multishot++;
145 break;
146 case PM_RANGER:
147 /* arbitrary; encourage use of other missiles beside daggers */
148 if (skill != P_DAGGER)
149 multishot++;
150 break;
151 case PM_ROGUE:
152 /* possibly should add knives... */
153 if (skill == P_DAGGER)
154 multishot++;
155 break;
156 case PM_SAMURAI:
157 /* role-specific launcher and its ammo */
158 if (obj->otyp == YA && uwep && uwep->otyp == YUMI)
159 multishot++;
160 break;
161 default:
162 break; /* No bonus */
163 }
164 /* ...or using their race's special bow; no bonus for spears */
165 if (!weakmultishot)
166 switch (Race_switch) {
167 case PM_ELF:
168 if (obj->otyp == ELVEN_ARROW && uwep
169 && uwep->otyp == ELVEN_BOW)
170 multishot++;
171 break;
172 case PM_ORC:
173 if (obj->otyp == ORCISH_ARROW && uwep
174 && uwep->otyp == ORCISH_BOW)
175 multishot++;
176 break;
177 case PM_GNOME:
178 /* arbitrary; there isn't any gnome-specific gear */
179 if (skill == -P_CROSSBOW)
180 multishot++;
181 break;
182 case PM_HUMAN:
183 case PM_DWARF:
184 default:
185 break; /* No bonus */
186 }
187
188 /* crossbows are slow to load and probably shouldn't allow multiple
189 shots at all, but that would result in players never using them;
190 instead, high strength is necessary to load and shoot quickly */
191 if (multishot > 1 && skill == -P_CROSSBOW
192 && ammo_and_launcher(obj, uwep)
193 && (int) ACURRSTR < (Race_if(PM_GNOME) ? 16 : 18))
194 multishot = rnd(multishot);
195
196 multishot = rnd(multishot);
197 if ((long) multishot > obj->quan)
198 multishot = (int) obj->quan;
199 if (shotlimit > 0 && multishot > shotlimit)
200 multishot = shotlimit;
201 }
202
203 m_shot.s = ammo_and_launcher(obj, uwep) ? TRUE : FALSE;
204 /* give a message if shooting more than one, or if player
205 attempted to specify a count */
206 if (multishot > 1 || shotlimit > 0) {
207 /* "You shoot N arrows." or "You throw N daggers." */
208 You("%s %d %s.", m_shot.s ? "shoot" : "throw",
209 multishot, /* (might be 1 if player gave shotlimit) */
210 (multishot == 1) ? singular(obj, xname) : xname(obj));
211 }
212
213 wep_mask = obj->owornmask;
214 m_shot.o = obj->otyp;
215 m_shot.n = multishot;
216 for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) {
217 twoweap = u.twoweap;
218 /* split this object off from its slot if necessary */
219 if (obj->quan > 1L) {
220 otmp = splitobj(obj, 1L);
221 } else {
222 otmp = obj;
223 if (otmp->owornmask)
224 remove_worn_item(otmp, FALSE);
225 }
226 freeinv(otmp);
227 throwit(otmp, wep_mask, twoweap);
228 }
229 m_shot.n = m_shot.i = 0;
230 m_shot.o = STRANGE_OBJECT;
231 m_shot.s = FALSE;
232
233 return 1;
234 }
235
236 /* common to dothrow() and dofire() */
237 STATIC_OVL boolean
ok_to_throw(shotlimit_p)238 ok_to_throw(shotlimit_p)
239 int *shotlimit_p; /* (see dothrow()) */
240 {
241 /* kludge to work around parse()'s pre-decrement of `multi' */
242 *shotlimit_p = (multi || save_cm) ? multi + 1 : 0;
243 multi = 0; /* reset; it's been used up */
244
245 if (notake(youmonst.data)) {
246 You("are physically incapable of throwing or shooting anything.");
247 return FALSE;
248 } else if (nohands(youmonst.data)) {
249 You_cant("throw or shoot without hands."); /* not body_part(HAND) */
250 return FALSE;
251 /*[what about !freehand(), aside from cursed missile launcher?]*/
252 }
253 if (check_capacity((char *) 0))
254 return FALSE;
255 return TRUE;
256 }
257
258 /* t command - throw */
259 int
dothrow()260 dothrow()
261 {
262 register struct obj *obj;
263 int shotlimit;
264
265 /*
266 * Since some characters shoot multiple missiles at one time,
267 * allow user to specify a count prefix for 'f' or 't' to limit
268 * number of items thrown (to avoid possibly hitting something
269 * behind target after killing it, or perhaps to conserve ammo).
270 *
271 * Prior to 3.3.0, command ``3t'' meant ``t(shoot) t(shoot) t(shoot)''
272 * and took 3 turns. Now it means ``t(shoot at most 3 missiles)''.
273 *
274 * [3.6.0: shot count setup has been moved into ok_to_throw().]
275 */
276 if (!ok_to_throw(&shotlimit))
277 return 0;
278
279 obj = getobj(uslinging() ? bullets : toss_objs, "throw");
280 /* it is also possible to throw food */
281 /* (or jewels, or iron balls... ) */
282
283 return obj ? throw_obj(obj, shotlimit) : 0;
284 }
285
286 /* KMH -- Automatically fill quiver */
287 /* Suggested by Jeffrey Bay <jbay@convex.hp.com> */
288 static void
autoquiver()289 autoquiver()
290 {
291 struct obj *otmp, *oammo = 0, *omissile = 0, *omisc = 0, *altammo = 0;
292
293 if (uquiver)
294 return;
295
296 /* Scan through the inventory */
297 for (otmp = invent; otmp; otmp = otmp->nobj) {
298 if (otmp->owornmask || otmp->oartifact || !otmp->dknown) {
299 ; /* Skip it */
300 } else if (otmp->otyp == ROCK
301 /* seen rocks or known flint or known glass */
302 || (otmp->otyp == FLINT
303 && objects[otmp->otyp].oc_name_known)
304 || (otmp->oclass == GEM_CLASS
305 && objects[otmp->otyp].oc_material == GLASS
306 && objects[otmp->otyp].oc_name_known)) {
307 if (uslinging())
308 oammo = otmp;
309 else if (ammo_and_launcher(otmp, uswapwep))
310 altammo = otmp;
311 else if (!omisc)
312 omisc = otmp;
313 } else if (otmp->oclass == GEM_CLASS) {
314 ; /* skip non-rock gems--they're ammo but
315 player has to select them explicitly */
316 } else if (is_ammo(otmp)) {
317 if (ammo_and_launcher(otmp, uwep))
318 /* Ammo matched with launcher (bow+arrow, crossbow+bolt) */
319 oammo = otmp;
320 else if (ammo_and_launcher(otmp, uswapwep))
321 altammo = otmp;
322 else
323 /* Mismatched ammo (no better than an ordinary weapon) */
324 omisc = otmp;
325 } else if (is_missile(otmp)) {
326 /* Missile (dart, shuriken, etc.) */
327 omissile = otmp;
328 } else if (otmp->oclass == WEAPON_CLASS && throwing_weapon(otmp)) {
329 /* Ordinary weapon */
330 if (objects[otmp->otyp].oc_skill == P_DAGGER && !omissile)
331 omissile = otmp;
332 else
333 omisc = otmp;
334 }
335 }
336
337 /* Pick the best choice */
338 if (oammo)
339 setuqwep(oammo);
340 else if (omissile)
341 setuqwep(omissile);
342 else if (altammo)
343 setuqwep(altammo);
344 else if (omisc)
345 setuqwep(omisc);
346
347 return;
348 }
349
350 /* f command -- fire: throw from the quiver */
351 int
dofire()352 dofire()
353 {
354 int shotlimit;
355 struct obj *obj;
356
357 /*
358 * Same as dothrow(), except we use quivered missile instead
359 * of asking what to throw/shoot.
360 *
361 * If quiver is empty, we use autoquiver to fill it when the
362 * corresponding option is on. If the option is off or if
363 * autoquiver doesn't select anything, we ask what to throw.
364 * Then we put the chosen item into the quiver slot unless
365 * it is already in another slot. [Matters most if it is a
366 * stack but also matters for single item if this throw gets
367 * aborted (ESC at the direction prompt). Already wielded
368 * item is excluded because wielding might be necessary
369 * (Mjollnir) or make the throw behave differently (aklys),
370 * and alt-wielded item is excluded because switching slots
371 * would end two-weapon combat even if throw gets aborted.]
372 */
373 if (!ok_to_throw(&shotlimit))
374 return 0;
375
376 if ((obj = uquiver) == 0) {
377 if (!flags.autoquiver) {
378 You("have no ammunition readied.");
379 } else {
380 autoquiver();
381 if ((obj = uquiver) == 0)
382 You("have nothing appropriate for your quiver.");
383 }
384 /* if autoquiver is disabled or has failed, prompt for missile;
385 fill quiver with it if it's not wielded or worn */
386 if (!obj) {
387 /* in case we're using ^A to repeat prior 'f' command, don't
388 use direction of previous throw as getobj()'s choice here */
389 in_doagain = 0;
390 /* choose something from inventory, then usually quiver it */
391 obj = getobj(uslinging() ? bullets : toss_objs, "throw");
392 /* Q command doesn't allow gold in quiver */
393 if (obj && !obj->owornmask && obj->oclass != COIN_CLASS)
394 setuqwep(obj); /* demi-autoquiver */
395 }
396 /* give feedback if quiver has now been filled */
397 if (uquiver) {
398 uquiver->owornmask &= ~W_QUIVER; /* less verbose */
399 prinv("You ready:", uquiver, 0L);
400 uquiver->owornmask |= W_QUIVER;
401 }
402 }
403
404 return obj ? throw_obj(obj, shotlimit) : 0;
405 }
406
407 /* if in midst of multishot shooting/throwing, stop early */
408 void
endmultishot(verbose)409 endmultishot(verbose)
410 boolean verbose;
411 {
412 if (m_shot.i < m_shot.n) {
413 if (verbose && !context.mon_moving) {
414 You("stop %s after the %d%s %s.",
415 m_shot.s ? "firing" : "throwing", m_shot.i, ordin(m_shot.i),
416 m_shot.s ? "shot" : "toss");
417 }
418 m_shot.n = m_shot.i; /* make current shot be the last */
419 }
420 }
421
422 /* Object hits floor at hero's feet.
423 Called from drop(), throwit(), hold_another_object(). */
424 void
hitfloor(obj,verbosely)425 hitfloor(obj, verbosely)
426 struct obj *obj;
427 boolean verbosely; /* usually True; False if caller has given drop message */
428 {
429 if (IS_SOFT(levl[u.ux][u.uy].typ) || u.uinwater || u.uswallow) {
430 dropy(obj);
431 return;
432 }
433 if (IS_ALTAR(levl[u.ux][u.uy].typ))
434 doaltarobj(obj);
435 else if (verbosely)
436 pline("%s %s the %s.", Doname2(obj), otense(obj, "hit"),
437 surface(u.ux, u.uy));
438
439 if (hero_breaks(obj, u.ux, u.uy, TRUE))
440 return;
441 if (ship_object(obj, u.ux, u.uy, FALSE))
442 return;
443 dropz(obj, TRUE);
444 }
445
446 /*
447 * Walk a path from src_cc to dest_cc, calling a proc for each location
448 * except the starting one. If the proc returns FALSE, stop walking
449 * and return FALSE. If stopped early, dest_cc will be the location
450 * before the failed callback.
451 */
452 boolean
walk_path(src_cc,dest_cc,check_proc,arg)453 walk_path(src_cc, dest_cc, check_proc, arg)
454 coord *src_cc;
455 coord *dest_cc;
456 boolean FDECL((*check_proc), (genericptr_t, int, int));
457 genericptr_t arg;
458 {
459 int x, y, dx, dy, x_change, y_change, err, i, prev_x, prev_y;
460 boolean keep_going = TRUE;
461
462 /* Use Bresenham's Line Algorithm to walk from src to dest.
463 *
464 * This should be replaced with a more versatile algorithm
465 * since it handles slanted moves in a suboptimal way.
466 * Going from 'x' to 'y' needs to pass through 'z', and will
467 * fail if there's an obstable there, but it could choose to
468 * pass through 'Z' instead if that way imposes no obstacle.
469 * ..y .Zy
470 * xz. vs x..
471 * Perhaps we should check both paths and accept whichever
472 * one isn't blocked. But then multiple zigs and zags could
473 * potentially produce a meandering path rather than the best
474 * attempt at a straight line. And (*check_proc)() would
475 * need to work more like 'travel', distinguishing between
476 * testing a possible move and actually attempting that move.
477 */
478 dx = dest_cc->x - src_cc->x;
479 dy = dest_cc->y - src_cc->y;
480 prev_x = x = src_cc->x;
481 prev_y = y = src_cc->y;
482
483 if (dx < 0) {
484 x_change = -1;
485 dx = -dx;
486 } else
487 x_change = 1;
488 if (dy < 0) {
489 y_change = -1;
490 dy = -dy;
491 } else
492 y_change = 1;
493
494 i = err = 0;
495 if (dx < dy) {
496 while (i++ < dy) {
497 prev_x = x;
498 prev_y = y;
499 y += y_change;
500 err += dx << 1;
501 if (err > dy) {
502 x += x_change;
503 err -= dy << 1;
504 }
505 /* check for early exit condition */
506 if (!(keep_going = (*check_proc)(arg, x, y)))
507 break;
508 }
509 } else {
510 while (i++ < dx) {
511 prev_x = x;
512 prev_y = y;
513 x += x_change;
514 err += dy << 1;
515 if (err > dx) {
516 y += y_change;
517 err -= dx << 1;
518 }
519 /* check for early exit condition */
520 if (!(keep_going = (*check_proc)(arg, x, y)))
521 break;
522 }
523 }
524
525 if (keep_going)
526 return TRUE; /* successful */
527
528 dest_cc->x = prev_x;
529 dest_cc->y = prev_y;
530 return FALSE;
531 }
532
533 /* hack for hurtle_step() -- it ought to be changed to take an argument
534 indicating lev/fly-to-dest vs lev/fly-to-dest-minus-one-land-on-dest
535 vs drag-to-dest; original callers use first mode, jumping wants second,
536 grappling hook backfire and thrown chained ball need third */
537 boolean
hurtle_jump(arg,x,y)538 hurtle_jump(arg, x, y)
539 genericptr_t arg;
540 int x, y;
541 {
542 boolean res;
543 long save_EWwalking = EWwalking;
544
545 /* prevent jumping over water from being placed in that water */
546 EWwalking |= I_SPECIAL;
547 res = hurtle_step(arg, x, y);
548 EWwalking = save_EWwalking;
549 return res;
550 }
551
552 /*
553 * Single step for the hero flying through the air from jumping, flying,
554 * etc. Called from hurtle() and jump() via walk_path(). We expect the
555 * argument to be a pointer to an integer -- the range -- which is
556 * used in the calculation of points off if we hit something.
557 *
558 * Bumping into monsters won't cause damage but will wake them and make
559 * them angry. Auto-pickup isn't done, since you don't have control over
560 * your movements at the time.
561 *
562 * Possible additions/changes:
563 * o really attack monster if we hit one
564 * o set stunned if we hit a wall or door
565 * o reset nomul when we stop
566 * o creepy feeling if pass through monster (if ever implemented...)
567 * o bounce off walls
568 * o let jumps go over boulders
569 */
570 boolean
hurtle_step(arg,x,y)571 hurtle_step(arg, x, y)
572 genericptr_t arg;
573 int x, y;
574 {
575 int ox, oy, *range = (int *) arg;
576 struct obj *obj;
577 struct monst *mon;
578 boolean may_pass = TRUE, via_jumping, stopping_short;
579 struct trap *ttmp;
580 int dmg = 0;
581
582 if (!isok(x, y)) {
583 You_feel("the spirits holding you back.");
584 return FALSE;
585 } else if (!in_out_region(x, y)) {
586 return FALSE;
587 } else if (*range == 0) {
588 return FALSE; /* previous step wants to stop now */
589 }
590 via_jumping = (EWwalking & I_SPECIAL) != 0L;
591 stopping_short = (via_jumping && *range < 2);
592
593 if (!Passes_walls || !(may_pass = may_passwall(x, y))) {
594 boolean odoor_diag = (IS_DOOR(levl[x][y].typ)
595 && (levl[x][y].doormask & D_ISOPEN)
596 && (u.ux - x) && (u.uy - y));
597
598 if (IS_ROCK(levl[x][y].typ) || closed_door(x, y) || odoor_diag) {
599 const char *s;
600
601 if (odoor_diag)
602 You("hit the door edge!");
603 pline("Ouch!");
604 if (IS_TREE(levl[x][y].typ))
605 s = "bumping into a tree";
606 else if (IS_ROCK(levl[x][y].typ))
607 s = "bumping into a wall";
608 else
609 s = "bumping into a door";
610 dmg = rnd(2 + *range);
611 losehp(Maybe_Half_Phys(dmg), s, KILLED_BY);
612 wake_nearto(x,y, 10);
613 return FALSE;
614 }
615 if (levl[x][y].typ == IRONBARS) {
616 You("crash into some iron bars. Ouch!");
617 dmg = rnd(2 + *range);
618 losehp(Maybe_Half_Phys(dmg), "crashing into iron bars",
619 KILLED_BY);
620 wake_nearto(x,y, 20);
621 return FALSE;
622 }
623 if ((obj = sobj_at(BOULDER, x, y)) != 0) {
624 You("bump into a %s. Ouch!", xname(obj));
625 dmg = rnd(2 + *range);
626 losehp(Maybe_Half_Phys(dmg), "bumping into a boulder", KILLED_BY);
627 wake_nearto(x,y, 10);
628 return FALSE;
629 }
630 if (!may_pass) {
631 /* did we hit a no-dig non-wall position? */
632 You("smack into something!");
633 dmg = rnd(2 + *range);
634 losehp(Maybe_Half_Phys(dmg), "touching the edge of the universe",
635 KILLED_BY);
636 wake_nearto(x,y, 10);
637 return FALSE;
638 }
639 if ((u.ux - x) && (u.uy - y) && bad_rock(youmonst.data, u.ux, y)
640 && bad_rock(youmonst.data, x, u.uy)) {
641 boolean too_much = (invent && (inv_weight() + weight_cap() > 600));
642
643 /* Move at a diagonal. */
644 if (bigmonst(youmonst.data) || too_much) {
645 You("%sget forcefully wedged into a crevice.",
646 too_much ? "and all your belongings " : "");
647 dmg = rnd(2 + *range);
648 losehp(Maybe_Half_Phys(dmg), "wedging into a narrow crevice",
649 KILLED_BY);
650 wake_nearto(x,y, 10);
651 return FALSE;
652 }
653 }
654 }
655
656 if ((mon = m_at(x, y)) != 0
657 #if 0 /* we can't include these two exceptions unless we know we're
658 * going to end up past the current spot rather than on it;
659 * for that, we need to know that the range is not exhausted
660 * and also that the next spot doesn't contain an obstacle */
661 && !(mon->mundetected && hides_under(mon) && (Flying || Levitation))
662 && !(mon->mundetected && mon->data->mlet == S_EEL
663 && (Flying || Levitation || Wwalking))
664 #endif
665 ) {
666 const char *mnam, *pronoun;
667 int glyph = glyph_at(x, y);
668
669 mon->mundetected = 0; /* wakeup() will handle mimic */
670 mnam = a_monnam(mon); /* after unhiding */
671 pronoun = noit_mhim(mon);
672 if (!strcmp(mnam, "it")) {
673 mnam = !strcmp(pronoun, "it") ? "something" : "someone";
674 }
675 if (!glyph_is_monster(glyph) && !glyph_is_invisible(glyph))
676 You("find %s by bumping into %s.", mnam, pronoun);
677 else
678 You("bump into %s.", mnam);
679 wakeup(mon, FALSE);
680 if (!canspotmon(mon))
681 map_invisible(mon->mx, mon->my);
682 setmangry(mon, FALSE);
683 wake_nearto(x, y, 10);
684 return FALSE;
685 }
686
687 if ((u.ux - x) && (u.uy - y)
688 && bad_rock(youmonst.data, u.ux, y)
689 && bad_rock(youmonst.data, x, u.uy)) {
690 /* Move at a diagonal. */
691 if (Sokoban) {
692 You("come to an abrupt halt!");
693 return FALSE;
694 }
695 }
696
697 /* Caller has already determined that dragging the ball is allowed */
698 if (Punished && uball->where == OBJ_FLOOR) {
699 int bc_control;
700 xchar ballx, bally, chainx, chainy;
701 boolean cause_delay;
702
703 if (drag_ball(x, y, &bc_control, &ballx, &bally, &chainx,
704 &chainy, &cause_delay, TRUE))
705 move_bc(0, bc_control, ballx, bally, chainx, chainy);
706 }
707
708 ox = u.ux;
709 oy = u.uy;
710 u_on_newpos(x, y); /* set u.<ux,uy>, u.usteed-><mx,my>; cliparound(); */
711 newsym(ox, oy); /* update old position */
712 vision_recalc(1); /* update for new position */
713 flush_screen(1);
714 /* if terrain type changes, levitation or flying might become blocked
715 or unblocked; might issue message, so do this after map+vision has
716 been updated for new location instead of right after u_on_newpos() */
717 if (levl[u.ux][u.uy].typ != levl[ox][oy].typ)
718 switch_terrain();
719
720 if (is_pool(x, y) && !u.uinwater) {
721 if ((Is_waterlevel(&u.uz) && levl[x][y].typ == WATER)
722 || !(Levitation || Flying || Wwalking)) {
723 multi = 0; /* can move, so drown() allows crawling out of water */
724 (void) drown();
725 return FALSE;
726 } else if (!Is_waterlevel(&u.uz) && !stopping_short) {
727 Norep("You move over %s.", an(is_moat(x, y) ? "moat" : "pool"));
728 }
729 } else if (is_lava(x, y) && !stopping_short) {
730 Norep("You move over some lava.");
731 }
732
733 /* FIXME:
734 * Each trap should really trigger on the recoil if it would
735 * trigger during normal movement. However, not all the possible
736 * side-effects of this are tested [as of 3.4.0] so we trigger
737 * those that we have tested, and offer a message for the ones
738 * that we have not yet tested.
739 */
740 if ((ttmp = t_at(x, y)) != 0) {
741 if (stopping_short) {
742 ; /* see the comment above hurtle_jump() */
743 } else if (ttmp->ttyp == MAGIC_PORTAL) {
744 dotrap(ttmp, 0);
745 return FALSE;
746 } else if (ttmp->ttyp == VIBRATING_SQUARE) {
747 pline("The ground vibrates as you pass it.");
748 dotrap(ttmp, 0); /* doesn't print messages */
749 } else if (ttmp->ttyp == FIRE_TRAP) {
750 dotrap(ttmp, 0);
751 } else if ((is_pit(ttmp->ttyp) || is_hole(ttmp->ttyp))
752 && Sokoban) {
753 /* air currents overcome the recoil in Sokoban;
754 when jumping, caller performs last step and enters trap */
755 if (!via_jumping)
756 dotrap(ttmp, 0);
757 *range = 0;
758 return TRUE;
759 } else {
760 if (ttmp->tseen)
761 You("pass right over %s.",
762 an(defsyms[trap_to_defsym(ttmp->ttyp)].explanation));
763 }
764 }
765 if (--*range < 0) /* make sure our range never goes negative */
766 *range = 0;
767 if (*range != 0)
768 delay_output();
769 return TRUE;
770 }
771
772 STATIC_OVL boolean
mhurtle_step(arg,x,y)773 mhurtle_step(arg, x, y)
774 genericptr_t arg;
775 int x, y;
776 {
777 struct monst *mon = (struct monst *) arg;
778
779 /* TODO: Treat walls, doors, iron bars, pools, lava, etc. specially
780 * rather than just stopping before.
781 */
782 if (goodpos(x, y, mon, 0) && m_in_out_region(mon, x, y)) {
783 remove_monster(mon->mx, mon->my);
784 newsym(mon->mx, mon->my);
785 place_monster(mon, x, y);
786 newsym(mon->mx, mon->my);
787 set_apparxy(mon);
788 (void) mintrap(mon);
789 return TRUE;
790 }
791 return FALSE;
792 }
793
794 /*
795 * The player moves through the air for a few squares as a result of
796 * throwing or kicking something.
797 *
798 * dx and dy should be the direction of the hurtle, not of the original
799 * kick or throw and be only.
800 */
801 void
hurtle(dx,dy,range,verbose)802 hurtle(dx, dy, range, verbose)
803 int dx, dy, range;
804 boolean verbose;
805 {
806 coord uc, cc;
807
808 /* The chain is stretched vertically, so you shouldn't be able to move
809 * very far diagonally. The premise that you should be able to move one
810 * spot leads to calculations that allow you to only move one spot away
811 * from the ball, if you are levitating over the ball, or one spot
812 * towards the ball, if you are at the end of the chain. Rather than
813 * bother with all of that, assume that there is no slack in the chain
814 * for diagonal movement, give the player a message and return.
815 */
816 if (Punished && !carried(uball)) {
817 You_feel("a tug from the iron ball.");
818 nomul(0);
819 return;
820 } else if (u.utrap) {
821 You("are anchored by the %s.",
822 u.utraptype == TT_WEB
823 ? "web"
824 : u.utraptype == TT_LAVA
825 ? hliquid("lava")
826 : u.utraptype == TT_INFLOOR
827 ? surface(u.ux, u.uy)
828 : u.utraptype == TT_BURIEDBALL ? "buried ball"
829 : "trap");
830 nomul(0);
831 return;
832 }
833
834 /* make sure dx and dy are [-1,0,1] */
835 dx = sgn(dx);
836 dy = sgn(dy);
837
838 if (!range || (!dx && !dy) || u.ustuck)
839 return; /* paranoia */
840
841 nomul(-range);
842 multi_reason = "moving through the air";
843 nomovemsg = ""; /* it just happens */
844 if (verbose)
845 You("%s in the opposite direction.", range > 1 ? "hurtle" : "float");
846 /* if we're in the midst of shooting multiple projectiles, stop */
847 endmultishot(TRUE);
848 sokoban_guilt();
849 uc.x = u.ux;
850 uc.y = u.uy;
851 /* this setting of cc is only correct if dx and dy are [-1,0,1] only */
852 cc.x = u.ux + (dx * range);
853 cc.y = u.uy + (dy * range);
854 (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t) &range);
855 }
856
857 /* Move a monster through the air for a few squares. */
858 void
mhurtle(mon,dx,dy,range)859 mhurtle(mon, dx, dy, range)
860 struct monst *mon;
861 int dx, dy, range;
862 {
863 coord mc, cc;
864
865 /* At the very least, debilitate the monster */
866 mon->movement = 0;
867 mon->mstun = 1;
868
869 /* Is the monster stuck or too heavy to push?
870 * (very large monsters have too much inertia, even floaters and flyers)
871 */
872 if (mon->data->msize >= MZ_HUGE || mon == u.ustuck || mon->mtrapped)
873 return;
874
875 /* Make sure dx and dy are [-1,0,1] */
876 dx = sgn(dx);
877 dy = sgn(dy);
878 if (!range || (!dx && !dy))
879 return; /* paranoia */
880 /* don't let grid bugs be hurtled diagonally */
881 if (dx && dy && NODIAG(monsndx(mon->data)))
882 return;
883
884 /* Send the monster along the path */
885 mc.x = mon->mx;
886 mc.y = mon->my;
887 cc.x = mon->mx + (dx * range);
888 cc.y = mon->my + (dy * range);
889 (void) walk_path(&mc, &cc, mhurtle_step, (genericptr_t) mon);
890 return;
891 }
892
893 STATIC_OVL void
check_shop_obj(obj,x,y,broken)894 check_shop_obj(obj, x, y, broken)
895 struct obj *obj;
896 xchar x, y;
897 boolean broken;
898 {
899 boolean costly_xy;
900 struct monst *shkp = shop_keeper(*u.ushops);
901
902 if (!shkp)
903 return;
904
905 costly_xy = costly_spot(x, y);
906 if (broken || !costly_xy || *in_rooms(x, y, SHOPBASE) != *u.ushops) {
907 /* thrown out of a shop or into a different shop */
908 if (is_unpaid(obj))
909 (void) stolen_value(obj, u.ux, u.uy, (boolean) shkp->mpeaceful,
910 FALSE);
911 if (broken)
912 obj->no_charge = 1;
913 } else if (costly_xy) {
914 char *oshops = in_rooms(x, y, SHOPBASE);
915
916 /* ushops0: in case we threw while levitating and recoiled
917 out of shop (most likely to the shk's spot in front of door) */
918 if (*oshops == *u.ushops || *oshops == *u.ushops0) {
919 if (is_unpaid(obj))
920 subfrombill(obj, shkp);
921 else if (x != shkp->mx || y != shkp->my)
922 sellobj(obj, x, y);
923 }
924 }
925 }
926
927 /*
928 * Hero tosses an object upwards with appropriate consequences.
929 *
930 * Returns FALSE if the object is gone.
931 */
932 STATIC_OVL boolean
toss_up(obj,hitsroof)933 toss_up(obj, hitsroof)
934 struct obj *obj;
935 boolean hitsroof;
936 {
937 const char *action;
938 boolean petrifier = ((obj->otyp == EGG || obj->otyp == CORPSE)
939 && touch_petrifies(&mons[obj->corpsenm]));
940 /* note: obj->quan == 1 */
941
942 if (!has_ceiling(&u.uz)) {
943 action = "flies up into"; /* into "the sky" or "the water above" */
944 } else if (hitsroof) {
945 if (breaktest(obj)) {
946 pline("%s hits the %s.", Doname2(obj), ceiling(u.ux, u.uy));
947 breakmsg(obj, !Blind);
948 breakobj(obj, u.ux, u.uy, TRUE, TRUE);
949 return FALSE;
950 }
951 action = "hits";
952 } else {
953 action = "almost hits";
954 }
955 pline("%s %s the %s, then falls back on top of your %s.", Doname2(obj),
956 action, ceiling(u.ux, u.uy), body_part(HEAD));
957
958 /* object now hits you */
959
960 if (obj->oclass == POTION_CLASS) {
961 potionhit(&youmonst, obj, POTHIT_HERO_THROW);
962 } else if (breaktest(obj)) {
963 int otyp = obj->otyp;
964 int blindinc;
965
966 /* need to check for blindness result prior to destroying obj */
967 blindinc = ((otyp == CREAM_PIE || otyp == BLINDING_VENOM)
968 /* AT_WEAP is ok here even if attack type was AT_SPIT */
969 && can_blnd(&youmonst, &youmonst, AT_WEAP, obj))
970 ? rnd(25)
971 : 0;
972 breakmsg(obj, !Blind);
973 breakobj(obj, u.ux, u.uy, TRUE, TRUE);
974 obj = 0; /* it's now gone */
975 switch (otyp) {
976 case EGG:
977 if (petrifier && !Stone_resistance
978 && !(poly_when_stoned(youmonst.data)
979 && polymon(PM_STONE_GOLEM))) {
980 /* egg ends up "all over your face"; perhaps
981 visored helmet should still save you here */
982 if (uarmh)
983 Your("%s fails to protect you.", helm_simple_name(uarmh));
984 goto petrify;
985 }
986 /*FALLTHRU*/
987 case CREAM_PIE:
988 case BLINDING_VENOM:
989 pline("You've got it all over your %s!", body_part(FACE));
990 if (blindinc) {
991 if (otyp == BLINDING_VENOM && !Blind)
992 pline("It blinds you!");
993 u.ucreamed += blindinc;
994 make_blinded(Blinded + (long) blindinc, FALSE);
995 if (!Blind)
996 Your1(vision_clears);
997 }
998 break;
999 default:
1000 break;
1001 }
1002 return FALSE;
1003 } else { /* neither potion nor other breaking object */
1004 boolean less_damage = uarmh && is_metallic(uarmh), artimsg = FALSE;
1005 int dmg = dmgval(obj, &youmonst);
1006
1007 if (obj->oartifact)
1008 /* need a fake die roll here; rn1(18,2) avoids 1 and 20 */
1009 artimsg = artifact_hit((struct monst *) 0, &youmonst, obj, &dmg,
1010 rn1(18, 2));
1011
1012 if (!dmg) { /* probably wasn't a weapon; base damage on weight */
1013 dmg = (int) obj->owt / 100;
1014 if (dmg < 1)
1015 dmg = 1;
1016 else if (dmg > 6)
1017 dmg = 6;
1018 if (youmonst.data == &mons[PM_SHADE]
1019 && objects[obj->otyp].oc_material != SILVER)
1020 dmg = 0;
1021 }
1022 if (dmg > 1 && less_damage)
1023 dmg = 1;
1024 if (dmg > 0)
1025 dmg += u.udaminc;
1026 if (dmg < 0)
1027 dmg = 0; /* beware negative rings of increase damage */
1028 dmg = Maybe_Half_Phys(dmg);
1029
1030 if (uarmh) {
1031 if (less_damage && dmg < (Upolyd ? u.mh : u.uhp)) {
1032 if (!artimsg)
1033 pline("Fortunately, you are wearing a hard helmet.");
1034 /* helmet definitely protects you when it blocks petrification
1035 */
1036 } else if (!petrifier) {
1037 if (flags.verbose)
1038 Your("%s does not protect you.", helm_simple_name(uarmh));
1039 }
1040 } else if (petrifier && !Stone_resistance
1041 && !(poly_when_stoned(youmonst.data)
1042 && polymon(PM_STONE_GOLEM))) {
1043 petrify:
1044 killer.format = KILLED_BY;
1045 Strcpy(killer.name, "elementary physics"); /* "what goes up..." */
1046 You("turn to stone.");
1047 if (obj)
1048 dropy(obj); /* bypass most of hitfloor() */
1049 thrownobj = 0; /* now either gone or on floor */
1050 done(STONING);
1051 return obj ? TRUE : FALSE;
1052 }
1053 hitfloor(obj, TRUE);
1054 thrownobj = 0;
1055 losehp(Maybe_Half_Phys(dmg), "falling object", KILLED_BY_AN);
1056 }
1057 return TRUE;
1058 }
1059
1060 /* return true for weapon meant to be thrown; excludes ammo */
1061 boolean
throwing_weapon(obj)1062 throwing_weapon(obj)
1063 struct obj *obj;
1064 {
1065 return (boolean) (is_missile(obj) || is_spear(obj)
1066 /* daggers and knife (excludes scalpel) */
1067 || (is_blade(obj) && !is_sword(obj)
1068 && (objects[obj->otyp].oc_dir & PIERCE))
1069 /* special cases [might want to add AXE] */
1070 || obj->otyp == WAR_HAMMER || obj->otyp == AKLYS);
1071 }
1072
1073 /* the currently thrown object is returning to you (not for boomerangs) */
1074 STATIC_OVL void
sho_obj_return_to_u(obj)1075 sho_obj_return_to_u(obj)
1076 struct obj *obj;
1077 {
1078 /* might already be our location (bounced off a wall) */
1079 if ((u.dx || u.dy) && (bhitpos.x != u.ux || bhitpos.y != u.uy)) {
1080 int x = bhitpos.x - u.dx, y = bhitpos.y - u.dy;
1081
1082 tmp_at(DISP_FLASH, obj_to_glyph(obj, rn2_on_display_rng));
1083 while (isok(x,y) && (x != u.ux || y != u.uy)) {
1084 tmp_at(x, y);
1085 delay_output();
1086 x -= u.dx;
1087 y -= u.dy;
1088 }
1089 tmp_at(DISP_END, 0);
1090 }
1091 }
1092
1093 /* throw an object, NB: obj may be consumed in the process */
1094 void
throwit(obj,wep_mask,twoweap)1095 throwit(obj, wep_mask, twoweap)
1096 struct obj *obj;
1097 long wep_mask; /* used to re-equip returning boomerang */
1098 boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
1099 {
1100 register struct monst *mon;
1101 int range, urange;
1102 boolean crossbowing, clear_thrownobj = FALSE,
1103 impaired = (Confusion || Stunned || Blind
1104 || Hallucination || Fumbling),
1105 tethered_weapon = (obj->otyp == AKLYS && (wep_mask & W_WEP) != 0);
1106
1107 notonhead = FALSE; /* reset potentially stale value */
1108 if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) {
1109 boolean slipok = TRUE;
1110
1111 if (ammo_and_launcher(obj, uwep)) {
1112 pline("%s!", Tobjnam(obj, "misfire"));
1113 } else {
1114 /* only slip if it's greased or meant to be thrown */
1115 if (obj->greased || throwing_weapon(obj))
1116 /* BUG: this message is grammatically incorrect if obj has
1117 a plural name; greased gloves or boots for instance. */
1118 pline("%s as you throw it!", Tobjnam(obj, "slip"));
1119 else
1120 slipok = FALSE;
1121 }
1122 if (slipok) {
1123 u.dx = rn2(3) - 1;
1124 u.dy = rn2(3) - 1;
1125 if (!u.dx && !u.dy)
1126 u.dz = 1;
1127 impaired = TRUE;
1128 }
1129 }
1130
1131 if ((u.dx || u.dy || (u.dz < 1))
1132 && calc_capacity((int) obj->owt) > SLT_ENCUMBER
1133 && (Upolyd ? (u.mh < 5 && u.mh != u.mhmax)
1134 : (u.uhp < 10 && u.uhp != u.uhpmax))
1135 && obj->owt > (unsigned) ((Upolyd ? u.mh : u.uhp) * 2)
1136 && !Is_airlevel(&u.uz)) {
1137 You("have so little stamina, %s drops from your grasp.",
1138 the(xname(obj)));
1139 exercise(A_CON, FALSE);
1140 u.dx = u.dy = 0;
1141 u.dz = 1;
1142 }
1143
1144 thrownobj = obj;
1145 thrownobj->was_thrown = 1;
1146 iflags.returning_missile = ((obj->oartifact == ART_MJOLLNIR
1147 && Role_if(PM_VALKYRIE))
1148 || tethered_weapon) ? (genericptr_t) obj
1149 : (genericptr_t) 0;
1150 /* NOTE: No early returns after this point or returning_missile
1151 will be left with a stale pointer. */
1152
1153 if (u.uswallow) {
1154 if (obj == uball) {
1155 uball->ox = uchain->ox = u.ux;
1156 uball->oy = uchain->oy = u.uy;
1157 }
1158 mon = u.ustuck;
1159 bhitpos.x = mon->mx;
1160 bhitpos.y = mon->my;
1161 if (tethered_weapon)
1162 tmp_at(DISP_TETHER, obj_to_glyph(obj, rn2_on_display_rng));
1163 } else if (u.dz) {
1164 if (u.dz < 0
1165 /* Mjollnir must we wielded to be thrown--caller verifies this;
1166 aklys must we wielded as primary to return when thrown */
1167 && iflags.returning_missile
1168 && !impaired) {
1169 pline("%s the %s and returns to your hand!", Tobjnam(obj, "hit"),
1170 ceiling(u.ux, u.uy));
1171 obj = addinv(obj);
1172 (void) encumber_msg();
1173 if (obj->owornmask & W_QUIVER) /* in case addinv() autoquivered */
1174 setuqwep((struct obj *) 0);
1175 setuwep(obj);
1176 u.twoweap = twoweap;
1177 } else if (u.dz < 0) {
1178 (void) toss_up(obj, rn2(5) && !Underwater);
1179 } else if (u.dz > 0 && u.usteed && obj->oclass == POTION_CLASS
1180 && rn2(6)) {
1181 /* alternative to prayer or wand of opening/spell of knock
1182 for dealing with cursed saddle: throw holy water > */
1183 potionhit(u.usteed, obj, POTHIT_HERO_THROW);
1184 } else {
1185 hitfloor(obj, TRUE);
1186 }
1187 clear_thrownobj = TRUE;
1188 goto throwit_return;
1189
1190 } else if (obj->otyp == BOOMERANG && !Underwater) {
1191 if (Is_airlevel(&u.uz) || Levitation)
1192 hurtle(-u.dx, -u.dy, 1, TRUE);
1193 mon = boomhit(obj, u.dx, u.dy);
1194 if (mon == &youmonst) { /* the thing was caught */
1195 exercise(A_DEX, TRUE);
1196 obj = addinv(obj);
1197 (void) encumber_msg();
1198 if (wep_mask && !(obj->owornmask & wep_mask)) {
1199 setworn(obj, wep_mask);
1200 u.twoweap = twoweap;
1201 }
1202 clear_thrownobj = TRUE;
1203 goto throwit_return;
1204 }
1205 } else {
1206 /* crossbow range is independent of strength */
1207 crossbowing = (ammo_and_launcher(obj, uwep)
1208 && weapon_type(uwep) == P_CROSSBOW);
1209 urange = (crossbowing ? 18 : (int) ACURRSTR) / 2;
1210 /* balls are easy to throw or at least roll;
1211 * also, this insures the maximum range of a ball is greater
1212 * than 1, so the effects from throwing attached balls are
1213 * actually possible
1214 */
1215 if (obj->otyp == HEAVY_IRON_BALL)
1216 range = urange - (int) (obj->owt / 100);
1217 else
1218 range = urange - (int) (obj->owt / 40);
1219 if (obj == uball) {
1220 if (u.ustuck)
1221 range = 1;
1222 else if (range >= 5)
1223 range = 5;
1224 }
1225 if (range < 1)
1226 range = 1;
1227
1228 if (is_ammo(obj)) {
1229 if (ammo_and_launcher(obj, uwep)) {
1230 if (crossbowing)
1231 range = BOLT_LIM;
1232 else
1233 range++;
1234 } else if (obj->oclass != GEM_CLASS)
1235 range /= 2;
1236 }
1237
1238 if (Is_airlevel(&u.uz) || Levitation) {
1239 /* action, reaction... */
1240 urange -= range;
1241 if (urange < 1)
1242 urange = 1;
1243 range -= urange;
1244 if (range < 1)
1245 range = 1;
1246 }
1247
1248 if (obj->otyp == BOULDER)
1249 range = 20; /* you must be giant */
1250 else if (obj->oartifact == ART_MJOLLNIR)
1251 range = (range + 1) / 2; /* it's heavy */
1252 else if (tethered_weapon) /* primary weapon is aklys */
1253 /* if an aklys is going to return, range is limited by the
1254 length of the attached cord [implicit aspect of item] */
1255 range = min(range, BOLT_LIM / 2);
1256 else if (obj == uball && u.utrap && u.utraptype == TT_INFLOOR)
1257 range = 1;
1258
1259 if (Underwater)
1260 range = 1;
1261
1262 mon = bhit(u.dx, u.dy, range,
1263 tethered_weapon ? THROWN_TETHERED_WEAPON : THROWN_WEAPON,
1264 (int FDECL((*), (MONST_P, OBJ_P))) 0,
1265 (int FDECL((*), (OBJ_P, OBJ_P))) 0, &obj);
1266 thrownobj = obj; /* obj may be null now */
1267
1268 /* have to do this after bhit() so u.ux & u.uy are correct */
1269 if (Is_airlevel(&u.uz) || Levitation)
1270 hurtle(-u.dx, -u.dy, urange, TRUE);
1271
1272 if (!obj) {
1273 /* bhit display cleanup was left with this caller
1274 for tethered_weapon, but clean it up now since
1275 we're about to return */
1276 if (tethered_weapon)
1277 tmp_at(DISP_END, 0);
1278 goto throwit_return;
1279 }
1280 }
1281
1282 if (mon) {
1283 boolean obj_gone;
1284
1285 if (mon->isshk && obj->where == OBJ_MINVENT && obj->ocarry == mon) {
1286 clear_thrownobj = TRUE;
1287 goto throwit_return; /* alert shk caught it */
1288 }
1289 (void) snuff_candle(obj);
1290 notonhead = (bhitpos.x != mon->mx || bhitpos.y != mon->my);
1291 obj_gone = thitmonst(mon, obj);
1292 /* Monster may have been tamed; this frees old mon [obsolete] */
1293 mon = m_at(bhitpos.x, bhitpos.y);
1294
1295 /* [perhaps this should be moved into thitmonst or hmon] */
1296 if (mon && mon->isshk
1297 && (!inside_shop(u.ux, u.uy)
1298 || !index(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops)))
1299 hot_pursuit(mon);
1300
1301 if (obj_gone)
1302 thrownobj = (struct obj *) 0;
1303 }
1304
1305 if (!thrownobj) {
1306 /* missile has already been handled */
1307 if (tethered_weapon)
1308 tmp_at(DISP_END, 0);
1309 } else if (u.uswallow && !iflags.returning_missile) {
1310 swallowit:
1311 if (obj != uball)
1312 (void) mpickobj(u.ustuck, obj); /* clears 'thrownobj' */
1313 else
1314 clear_thrownobj = TRUE;
1315 goto throwit_return;
1316 } else {
1317 /* Mjollnir must be wielded to be thrown--caller verifies this;
1318 aklys must be wielded as primary to return when thrown */
1319 if (iflags.returning_missile) { /* Mjollnir or aklys */
1320 if (rn2(100)) {
1321 if (tethered_weapon)
1322 tmp_at(DISP_END, BACKTRACK);
1323 else
1324 sho_obj_return_to_u(obj); /* display its flight */
1325
1326 if (!impaired && rn2(100)) {
1327 pline("%s to your hand!", Tobjnam(obj, "return"));
1328 obj = addinv(obj);
1329 (void) encumber_msg();
1330 /* addinv autoquivers an aklys if quiver is empty;
1331 if obj is quivered, remove it before wielding */
1332 if (obj->owornmask & W_QUIVER)
1333 setuqwep((struct obj *) 0);
1334 setuwep(obj);
1335 u.twoweap = twoweap;
1336 if (cansee(bhitpos.x, bhitpos.y))
1337 newsym(bhitpos.x, bhitpos.y);
1338 } else {
1339 int dmg = rn2(2);
1340
1341 if (!dmg) {
1342 pline(Blind ? "%s lands %s your %s."
1343 : "%s back to you, landing %s your %s.",
1344 Blind ? Something : Tobjnam(obj, "return"),
1345 Levitation ? "beneath" : "at",
1346 makeplural(body_part(FOOT)));
1347 } else {
1348 dmg += rnd(3);
1349 pline(Blind ? "%s your %s!"
1350 : "%s back toward you, hitting your %s!",
1351 Tobjnam(obj, Blind ? "hit" : "fly"),
1352 body_part(ARM));
1353 if (obj->oartifact)
1354 (void) artifact_hit((struct monst *) 0, &youmonst,
1355 obj, &dmg, 0);
1356 losehp(Maybe_Half_Phys(dmg), killer_xname(obj),
1357 KILLED_BY);
1358 }
1359
1360 if (u.uswallow)
1361 goto swallowit;
1362 if (!ship_object(obj, u.ux, u.uy, FALSE))
1363 dropy(obj);
1364 }
1365 clear_thrownobj = TRUE;
1366 goto throwit_return;
1367 } else {
1368 if (tethered_weapon)
1369 tmp_at(DISP_END, 0);
1370 /* when this location is stepped on, the weapon will be
1371 auto-picked up due to 'obj->was_thrown' of 1;
1372 addinv() prevents thrown Mjollnir from being placed
1373 into the quiver slot, but an aklys will end up there if
1374 that slot is empty at the time; since hero will need to
1375 explicitly rewield the weapon to get throw-and-return
1376 capability back anyway, quivered or not shouldn't matter */
1377 pline("%s to return!", Tobjnam(obj, "fail"));
1378
1379 if (u.uswallow)
1380 goto swallowit;
1381 /* continue below with placing 'obj' at target location */
1382 }
1383 }
1384
1385 if ((!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ) && breaktest(obj))
1386 /* venom [via #monster to spit while poly'd] fails breaktest()
1387 but we want to force breakage even when location IS_SOFT() */
1388 || obj->oclass == VENOM_CLASS) {
1389 tmp_at(DISP_FLASH, obj_to_glyph(obj, rn2_on_display_rng));
1390 tmp_at(bhitpos.x, bhitpos.y);
1391 delay_output();
1392 tmp_at(DISP_END, 0);
1393 breakmsg(obj, cansee(bhitpos.x, bhitpos.y));
1394 breakobj(obj, bhitpos.x, bhitpos.y, TRUE, TRUE);
1395 clear_thrownobj = TRUE;
1396 goto throwit_return;
1397 }
1398 if (flooreffects(obj, bhitpos.x, bhitpos.y, "fall")) {
1399 clear_thrownobj = TRUE;
1400 goto throwit_return;
1401 }
1402 obj_no_longer_held(obj);
1403 if (mon && mon->isshk && is_pick(obj)) {
1404 if (cansee(bhitpos.x, bhitpos.y))
1405 pline("%s snatches up %s.", Monnam(mon), the(xname(obj)));
1406 if (*u.ushops || obj->unpaid)
1407 check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE);
1408 (void) mpickobj(mon, obj); /* may merge and free obj */
1409 clear_thrownobj = TRUE;
1410 goto throwit_return;
1411 }
1412 (void) snuff_candle(obj);
1413 if (!mon && ship_object(obj, bhitpos.x, bhitpos.y, FALSE)) {
1414 clear_thrownobj = TRUE;
1415 goto throwit_return;
1416 }
1417 thrownobj = (struct obj *) 0;
1418 place_object(obj, bhitpos.x, bhitpos.y);
1419 /* container contents might break;
1420 do so before turning ownership of thrownobj over to shk
1421 (container_impact_dmg handles item already owned by shop) */
1422 if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ))
1423 /* <x,y> is spot where you initiated throw, not bhitpos */
1424 container_impact_dmg(obj, u.ux, u.uy);
1425 /* charge for items thrown out of shop;
1426 shk takes possession for items thrown into one */
1427 if ((*u.ushops || obj->unpaid) && obj != uball)
1428 check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE);
1429
1430 stackobj(obj);
1431 if (obj == uball)
1432 drop_ball(bhitpos.x, bhitpos.y);
1433 if (cansee(bhitpos.x, bhitpos.y))
1434 newsym(bhitpos.x, bhitpos.y);
1435 if (obj_sheds_light(obj))
1436 vision_full_recalc = 1;
1437 }
1438
1439 throwit_return:
1440 iflags.returning_missile = (genericptr_t) 0;
1441 if (clear_thrownobj)
1442 thrownobj = (struct obj *) 0;
1443 return;
1444 }
1445
1446 /* an object may hit a monster; various factors adjust chance of hitting */
1447 int
omon_adj(mon,obj,mon_notices)1448 omon_adj(mon, obj, mon_notices)
1449 struct monst *mon;
1450 struct obj *obj;
1451 boolean mon_notices;
1452 {
1453 int tmp = 0;
1454
1455 /* size of target affects the chance of hitting */
1456 tmp += (mon->data->msize - MZ_MEDIUM); /* -2..+5 */
1457 /* sleeping target is more likely to be hit */
1458 if (mon->msleeping) {
1459 tmp += 2;
1460 if (mon_notices)
1461 mon->msleeping = 0;
1462 }
1463 /* ditto for immobilized target */
1464 if (!mon->mcanmove || !mon->data->mmove) {
1465 tmp += 4;
1466 if (mon_notices && mon->data->mmove && !rn2(10)) {
1467 mon->mcanmove = 1;
1468 mon->mfrozen = 0;
1469 }
1470 }
1471 /* some objects are more likely to hit than others */
1472 switch (obj->otyp) {
1473 case HEAVY_IRON_BALL:
1474 if (obj != uball)
1475 tmp += 2;
1476 break;
1477 case BOULDER:
1478 tmp += 6;
1479 break;
1480 default:
1481 if (obj->oclass == WEAPON_CLASS || is_weptool(obj)
1482 || obj->oclass == GEM_CLASS)
1483 tmp += hitval(obj, mon);
1484 break;
1485 }
1486 return tmp;
1487 }
1488
1489 /* thrown object misses target monster */
1490 STATIC_OVL void
tmiss(obj,mon,maybe_wakeup)1491 tmiss(obj, mon, maybe_wakeup)
1492 struct obj *obj;
1493 struct monst *mon;
1494 boolean maybe_wakeup;
1495 {
1496 const char *missile = mshot_xname(obj);
1497
1498 /* If the target can't be seen or doesn't look like a valid target,
1499 avoid "the arrow misses it," or worse, "the arrows misses the mimic."
1500 An attentive player will still notice that this is different from
1501 an arrow just landing short of any target (no message in that case),
1502 so will realize that there is a valid target here anyway. */
1503 if (!canseemon(mon) || (M_AP_TYPE(mon) && M_AP_TYPE(mon) != M_AP_MONSTER))
1504 pline("%s %s.", The(missile), otense(obj, "miss"));
1505 else
1506 miss(missile, mon);
1507 if (maybe_wakeup && !rn2(3))
1508 wakeup(mon, TRUE);
1509 return;
1510 }
1511
1512 #define quest_arti_hits_leader(obj, mon) \
1513 (obj->oartifact && is_quest_artifact(obj) \
1514 && mon->m_id == quest_status.leader_m_id)
1515
1516 /*
1517 * Object thrown by player arrives at monster's location.
1518 * Return 1 if obj has disappeared or otherwise been taken care of,
1519 * 0 if caller must take care of it.
1520 * Also used for kicked objects and for polearms/grapnel applied at range.
1521 */
1522 int
thitmonst(mon,obj)1523 thitmonst(mon, obj)
1524 register struct monst *mon;
1525 register struct obj *obj; /* thrownobj or kickedobj or uwep */
1526 {
1527 register int tmp; /* Base chance to hit */
1528 register int disttmp; /* distance modifier */
1529 int otyp = obj->otyp, hmode;
1530 boolean guaranteed_hit = (u.uswallow && mon == u.ustuck);
1531 int dieroll;
1532
1533 hmode = (obj == uwep) ? HMON_APPLIED
1534 : (obj == kickedobj) ? HMON_KICKED
1535 : HMON_THROWN;
1536
1537 /* Differences from melee weapons:
1538 *
1539 * Dex still gives a bonus, but strength does not.
1540 * Polymorphed players lacking attacks may still throw.
1541 * There's a base -1 to hit.
1542 * No bonuses for fleeing or stunned targets (they don't dodge
1543 * melee blows as readily, but dodging arrows is hard anyway).
1544 * Not affected by traps, etc.
1545 * Certain items which don't in themselves do damage ignore 'tmp'.
1546 * Distance and monster size affect chance to hit.
1547 */
1548 tmp = -1 + Luck + find_mac(mon) + u.uhitinc
1549 + maybe_polyd(youmonst.data->mlevel, u.ulevel);
1550 if (ACURR(A_DEX) < 4)
1551 tmp -= 3;
1552 else if (ACURR(A_DEX) < 6)
1553 tmp -= 2;
1554 else if (ACURR(A_DEX) < 8)
1555 tmp -= 1;
1556 else if (ACURR(A_DEX) >= 14)
1557 tmp += (ACURR(A_DEX) - 14);
1558
1559 /* Modify to-hit depending on distance; but keep it sane.
1560 * Polearms get a distance penalty even when wielded; it's
1561 * hard to hit at a distance.
1562 */
1563 disttmp = 3 - distmin(u.ux, u.uy, mon->mx, mon->my);
1564 if (disttmp < -4)
1565 disttmp = -4;
1566 tmp += disttmp;
1567
1568 /* gloves are a hindrance to proper use of bows */
1569 if (uarmg && uwep && objects[uwep->otyp].oc_skill == P_BOW) {
1570 switch (uarmg->otyp) {
1571 case GAUNTLETS_OF_POWER: /* metal */
1572 tmp -= 2;
1573 break;
1574 case GAUNTLETS_OF_FUMBLING:
1575 tmp -= 3;
1576 break;
1577 case LEATHER_GLOVES:
1578 case GAUNTLETS_OF_DEXTERITY:
1579 break;
1580 default:
1581 impossible("Unknown type of gloves (%d)", uarmg->otyp);
1582 break;
1583 }
1584 }
1585
1586 tmp += omon_adj(mon, obj, TRUE);
1587 if (is_orc(mon->data)
1588 && maybe_polyd(is_elf(youmonst.data), Race_if(PM_ELF)))
1589 tmp++;
1590 if (guaranteed_hit) {
1591 tmp += 1000; /* Guaranteed hit */
1592 }
1593
1594 if (obj->oclass == GEM_CLASS && is_unicorn(mon->data)) {
1595 if (mon->msleeping || !mon->mcanmove) {
1596 tmiss(obj, mon, FALSE);
1597 return 0;
1598 } else if (mon->mtame) {
1599 pline("%s catches and drops %s.", Monnam(mon), the(xname(obj)));
1600 return 0;
1601 } else {
1602 pline("%s catches %s.", Monnam(mon), the(xname(obj)));
1603 return gem_accept(mon, obj);
1604 }
1605 }
1606
1607 /* don't make game unwinnable if naive player throws artifact
1608 at leader... (kicked artifact is ok too; HMON_APPLIED could
1609 occur if quest artifact polearm or grapnel ever gets added) */
1610 if (hmode != HMON_APPLIED && quest_arti_hits_leader(obj, mon)) {
1611 /* AIS: changes to wakeup() means that it's now less inappropriate here
1612 than it used to be, but the manual version works just as well */
1613 mon->msleeping = 0;
1614 mon->mstrategy &= ~STRAT_WAITMASK;
1615
1616 if (mon->mcanmove) {
1617 pline("%s catches %s.", Monnam(mon), the(xname(obj)));
1618 if (mon->mpeaceful) {
1619 boolean next2u = monnear(mon, u.ux, u.uy);
1620
1621 finish_quest(obj); /* acknowledge quest completion */
1622 pline("%s %s %s back to you.", Monnam(mon),
1623 (next2u ? "hands" : "tosses"), the(xname(obj)));
1624 if (!next2u)
1625 sho_obj_return_to_u(obj);
1626 obj = addinv(obj); /* back into your inventory */
1627 (void) encumber_msg();
1628 } else {
1629 /* angry leader caught it and isn't returning it */
1630 if (*u.ushops || obj->unpaid) /* not very likely... */
1631 check_shop_obj(obj, mon->mx, mon->my, FALSE);
1632 (void) mpickobj(mon, obj);
1633 }
1634 return 1; /* caller doesn't need to place it */
1635 }
1636 return 0;
1637 }
1638
1639 dieroll = rnd(20);
1640
1641 if (obj->oclass == WEAPON_CLASS || is_weptool(obj)
1642 || obj->oclass == GEM_CLASS) {
1643 if (hmode == HMON_KICKED) {
1644 /* throwing adjustments and weapon skill bonus don't apply */
1645 tmp -= (is_ammo(obj) ? 5 : 3);
1646 } else if (is_ammo(obj)) {
1647 if (!ammo_and_launcher(obj, uwep)) {
1648 tmp -= 4;
1649 } else {
1650 tmp += uwep->spe - greatest_erosion(uwep);
1651 tmp += weapon_hit_bonus(uwep);
1652 if (uwep->oartifact)
1653 tmp += spec_abon(uwep, mon);
1654 /*
1655 * Elves and Samurais are highly trained w/bows,
1656 * especially their own special types of bow.
1657 * Polymorphing won't make you a bow expert.
1658 */
1659 if ((Race_if(PM_ELF) || Role_if(PM_SAMURAI))
1660 && (!Upolyd || your_race(youmonst.data))
1661 && objects[uwep->otyp].oc_skill == P_BOW) {
1662 tmp++;
1663 if (Race_if(PM_ELF) && uwep->otyp == ELVEN_BOW)
1664 tmp++;
1665 else if (Role_if(PM_SAMURAI) && uwep->otyp == YUMI)
1666 tmp++;
1667 }
1668 }
1669 } else { /* thrown non-ammo or applied polearm/grapnel */
1670 if (otyp == BOOMERANG) /* arbitrary */
1671 tmp += 4;
1672 else if (throwing_weapon(obj)) /* meant to be thrown */
1673 tmp += 2;
1674 else if (obj == thrownobj) /* not meant to be thrown */
1675 tmp -= 2;
1676 /* we know we're dealing with a weapon or weptool handled
1677 by WEAPON_SKILLS once ammo objects have been excluded */
1678 tmp += weapon_hit_bonus(obj);
1679 }
1680
1681 if (tmp >= dieroll) {
1682 boolean wasthrown = (thrownobj != 0),
1683 /* remember weapon attribute; hmon() might destroy obj */
1684 chopper = is_axe(obj);
1685
1686 /* attack hits mon */
1687 if (hmode == HMON_APPLIED)
1688 u.uconduct.weaphit++;
1689 if (hmon(mon, obj, hmode, dieroll)) { /* mon still alive */
1690 if (mon->wormno)
1691 cutworm(mon, bhitpos.x, bhitpos.y, chopper);
1692 }
1693 exercise(A_DEX, TRUE);
1694 /* if hero was swallowed and projectile killed the engulfer,
1695 'obj' got added to engulfer's inventory and then dropped,
1696 so we can't safely use that pointer anymore; it escapes
1697 the chance to be used up here... */
1698 if (wasthrown && !thrownobj)
1699 return 1;
1700
1701 /* projectiles other than magic stones sometimes disappear
1702 when thrown; projectiles aren't among the types of weapon
1703 that hmon() might have destroyed so obj is intact */
1704 if (objects[otyp].oc_skill < P_NONE
1705 && objects[otyp].oc_skill > -P_BOOMERANG
1706 && !objects[otyp].oc_magic) {
1707 /* we were breaking 2/3 of everything unconditionally.
1708 * we still don't want anything to survive unconditionally,
1709 * but we need ammo to stay around longer on average.
1710 */
1711 int broken, chance;
1712
1713 chance = 3 + greatest_erosion(obj) - obj->spe;
1714 if (chance > 1)
1715 broken = rn2(chance);
1716 else
1717 broken = !rn2(4);
1718 if (obj->blessed && !rnl(4))
1719 broken = 0;
1720
1721 if (broken) {
1722 if (*u.ushops || obj->unpaid)
1723 check_shop_obj(obj, bhitpos.x, bhitpos.y, TRUE);
1724 obfree(obj, (struct obj *) 0);
1725 return 1;
1726 }
1727 }
1728 passive_obj(mon, obj, (struct attack *) 0);
1729 } else {
1730 tmiss(obj, mon, TRUE);
1731 if (hmode == HMON_APPLIED)
1732 wakeup(mon, TRUE);
1733 }
1734
1735 } else if (otyp == HEAVY_IRON_BALL) {
1736 exercise(A_STR, TRUE);
1737 if (tmp >= dieroll) {
1738 int was_swallowed = guaranteed_hit;
1739
1740 exercise(A_DEX, TRUE);
1741 if (!hmon(mon, obj, hmode, dieroll)) { /* mon killed */
1742 if (was_swallowed && !u.uswallow && obj == uball)
1743 return 1; /* already did placebc() */
1744 }
1745 } else {
1746 tmiss(obj, mon, TRUE);
1747 }
1748
1749 } else if (otyp == BOULDER) {
1750 exercise(A_STR, TRUE);
1751 if (tmp >= dieroll) {
1752 exercise(A_DEX, TRUE);
1753 (void) hmon(mon, obj, hmode, dieroll);
1754 } else {
1755 tmiss(obj, mon, TRUE);
1756 }
1757
1758 } else if ((otyp == EGG || otyp == CREAM_PIE || otyp == BLINDING_VENOM
1759 || otyp == ACID_VENOM)
1760 && (guaranteed_hit || ACURR(A_DEX) > rnd(25))) {
1761 (void) hmon(mon, obj, hmode, dieroll);
1762 return 1; /* hmon used it up */
1763
1764 } else if (obj->oclass == POTION_CLASS
1765 && (guaranteed_hit || ACURR(A_DEX) > rnd(25))) {
1766 potionhit(mon, obj, POTHIT_HERO_THROW);
1767 return 1;
1768
1769 } else if (befriend_with_obj(mon->data, obj)
1770 || (mon->mtame && dogfood(mon, obj) <= ACCFOOD)) {
1771 if (tamedog(mon, obj)) {
1772 return 1; /* obj is gone */
1773 } else {
1774 tmiss(obj, mon, FALSE);
1775 mon->msleeping = 0;
1776 mon->mstrategy &= ~STRAT_WAITMASK;
1777 }
1778 } else if (guaranteed_hit) {
1779 /* this assumes that guaranteed_hit is due to swallowing */
1780 wakeup(mon, TRUE);
1781 if (obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])) {
1782 if (is_animal(u.ustuck->data)) {
1783 minstapetrify(u.ustuck, TRUE);
1784 /* Don't leave a cockatrice corpse available in a statue */
1785 if (!u.uswallow) {
1786 delobj(obj);
1787 return 1;
1788 }
1789 }
1790 }
1791 pline("%s into %s %s.", Tobjnam(obj, "vanish"),
1792 s_suffix(mon_nam(mon)),
1793 is_animal(u.ustuck->data) ? "entrails" : "currents");
1794 } else {
1795 tmiss(obj, mon, TRUE);
1796 }
1797
1798 return 0;
1799 }
1800
1801 STATIC_OVL int
gem_accept(mon,obj)1802 gem_accept(mon, obj)
1803 register struct monst *mon;
1804 register struct obj *obj;
1805 {
1806 char buf[BUFSZ];
1807 boolean is_buddy = sgn(mon->data->maligntyp) == sgn(u.ualign.type);
1808 boolean is_gem = objects[obj->otyp].oc_material == GEMSTONE;
1809 int ret = 0;
1810 static NEARDATA const char nogood[] = " is not interested in your junk.";
1811 static NEARDATA const char acceptgift[] = " accepts your gift.";
1812 static NEARDATA const char maybeluck[] = " hesitatingly";
1813 static NEARDATA const char noluck[] = " graciously";
1814 static NEARDATA const char addluck[] = " gratefully";
1815
1816 Strcpy(buf, Monnam(mon));
1817 mon->mpeaceful = 1;
1818 mon->mavenge = 0;
1819
1820 /* object properly identified */
1821 if (obj->dknown && objects[obj->otyp].oc_name_known) {
1822 if (is_gem) {
1823 if (is_buddy) {
1824 Strcat(buf, addluck);
1825 change_luck(5);
1826 } else {
1827 Strcat(buf, maybeluck);
1828 change_luck(rn2(7) - 3);
1829 }
1830 } else {
1831 Strcat(buf, nogood);
1832 goto nopick;
1833 }
1834 /* making guesses */
1835 } else if (has_oname(obj) || objects[obj->otyp].oc_uname) {
1836 if (is_gem) {
1837 if (is_buddy) {
1838 Strcat(buf, addluck);
1839 change_luck(2);
1840 } else {
1841 Strcat(buf, maybeluck);
1842 change_luck(rn2(3) - 1);
1843 }
1844 } else {
1845 Strcat(buf, nogood);
1846 goto nopick;
1847 }
1848 /* value completely unknown to @ */
1849 } else {
1850 if (is_gem) {
1851 if (is_buddy) {
1852 Strcat(buf, addluck);
1853 change_luck(1);
1854 } else {
1855 Strcat(buf, maybeluck);
1856 change_luck(rn2(3) - 1);
1857 }
1858 } else {
1859 Strcat(buf, noluck);
1860 }
1861 }
1862 Strcat(buf, acceptgift);
1863 if (*u.ushops || obj->unpaid)
1864 check_shop_obj(obj, mon->mx, mon->my, TRUE);
1865 (void) mpickobj(mon, obj); /* may merge and free obj */
1866 ret = 1;
1867
1868 nopick:
1869 if (!Blind)
1870 pline1(buf);
1871 if (!tele_restrict(mon))
1872 (void) rloc(mon, TRUE);
1873 return ret;
1874 }
1875
1876 /*
1877 * Comments about the restructuring of the old breaks() routine.
1878 *
1879 * There are now three distinct phases to object breaking:
1880 * breaktest() - which makes the check/decision about whether the
1881 * object is going to break.
1882 * breakmsg() - which outputs a message about the breakage,
1883 * appropriate for that particular object. Should
1884 * only be called after a positive breaktest().
1885 * on the object and, if it going to be called,
1886 * it must be called before calling breakobj().
1887 * Calling breakmsg() is optional.
1888 * breakobj() - which actually does the breakage and the side-effects
1889 * of breaking that particular object. This should
1890 * only be called after a positive breaktest() on the
1891 * object.
1892 *
1893 * Each of the above routines is currently static to this source module.
1894 * There are two routines callable from outside this source module which
1895 * perform the routines above in the correct sequence.
1896 *
1897 * hero_breaks() - called when an object is to be broken as a result
1898 * of something that the hero has done. (throwing it,
1899 * kicking it, etc.)
1900 * breaks() - called when an object is to be broken for some
1901 * reason other than the hero doing something to it.
1902 */
1903
1904 /*
1905 * The hero causes breakage of an object (throwing, dropping it, etc.)
1906 * Return 0 if the object didn't break, 1 if the object broke.
1907 */
1908 int
hero_breaks(obj,x,y,from_invent)1909 hero_breaks(obj, x, y, from_invent)
1910 struct obj *obj;
1911 xchar x, y; /* object location (ox, oy may not be right) */
1912 boolean from_invent; /* thrown or dropped by player; maybe on shop bill */
1913 {
1914 boolean in_view = Blind ? FALSE : (from_invent || cansee(x, y));
1915
1916 if (!breaktest(obj))
1917 return 0;
1918 breakmsg(obj, in_view);
1919 breakobj(obj, x, y, TRUE, from_invent);
1920 return 1;
1921 }
1922
1923 /*
1924 * The object is going to break for a reason other than the hero doing
1925 * something to it.
1926 * Return 0 if the object doesn't break, 1 if the object broke.
1927 */
1928 int
breaks(obj,x,y)1929 breaks(obj, x, y)
1930 struct obj *obj;
1931 xchar x, y; /* object location (ox, oy may not be right) */
1932 {
1933 boolean in_view = Blind ? FALSE : cansee(x, y);
1934
1935 if (!breaktest(obj))
1936 return 0;
1937 breakmsg(obj, in_view);
1938 breakobj(obj, x, y, FALSE, FALSE);
1939 return 1;
1940 }
1941
1942 void
release_camera_demon(obj,x,y)1943 release_camera_demon(obj, x, y)
1944 struct obj *obj;
1945 xchar x, y;
1946 {
1947 struct monst *mtmp;
1948 if (!rn2(3)
1949 && (mtmp = makemon(&mons[rn2(3) ? PM_HOMUNCULUS : PM_IMP], x, y,
1950 NO_MM_FLAGS)) != 0) {
1951 if (canspotmon(mtmp))
1952 pline("%s is released!", Hallucination
1953 ? An(rndmonnam(NULL))
1954 : "The picture-painting demon");
1955 mtmp->mpeaceful = !obj->cursed;
1956 set_malign(mtmp);
1957 }
1958 }
1959
1960 /*
1961 * Unconditionally break an object. Assumes all resistance checks
1962 * and break messages have been delivered prior to getting here.
1963 */
1964 void
breakobj(obj,x,y,hero_caused,from_invent)1965 breakobj(obj, x, y, hero_caused, from_invent)
1966 struct obj *obj;
1967 xchar x, y; /* object location (ox, oy may not be right) */
1968 boolean hero_caused; /* is this the hero's fault? */
1969 boolean from_invent;
1970 {
1971 boolean fracture = FALSE;
1972
1973 switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) {
1974 case MIRROR:
1975 if (hero_caused)
1976 change_luck(-2);
1977 break;
1978 case POT_WATER: /* really, all potions */
1979 obj->in_use = 1; /* in case it's fatal */
1980 if (obj->otyp == POT_OIL && obj->lamplit) {
1981 explode_oil(obj, x, y);
1982 } else if (distu(x, y) <= 2) {
1983 if (!breathless(youmonst.data) || haseyes(youmonst.data)) {
1984 if (obj->otyp != POT_WATER) {
1985 if (!breathless(youmonst.data)) {
1986 /* [what about "familiar odor" when known?] */
1987 You("smell a peculiar odor...");
1988 } else {
1989 const char *eyes = body_part(EYE);
1990
1991 if (eyecount(youmonst.data) != 1)
1992 eyes = makeplural(eyes);
1993 Your("%s %s.", eyes, vtense(eyes, "water"));
1994 }
1995 }
1996 potionbreathe(obj);
1997 }
1998 }
1999 /* monster breathing isn't handled... [yet?] */
2000 break;
2001 case EXPENSIVE_CAMERA:
2002 release_camera_demon(obj, x, y);
2003 break;
2004 case EGG:
2005 /* breaking your own eggs is bad luck */
2006 if (hero_caused && obj->spe && obj->corpsenm >= LOW_PM)
2007 change_luck((schar) -min(obj->quan, 5L));
2008 break;
2009 case BOULDER:
2010 case STATUE:
2011 /* caller will handle object disposition;
2012 we're just doing the shop theft handling */
2013 fracture = TRUE;
2014 break;
2015 default:
2016 break;
2017 }
2018
2019 if (hero_caused) {
2020 if (from_invent || obj->unpaid) {
2021 if (*u.ushops || obj->unpaid)
2022 check_shop_obj(obj, x, y, TRUE);
2023 } else if (!obj->no_charge && costly_spot(x, y)) {
2024 /* it is assumed that the obj is a floor-object */
2025 char *o_shop = in_rooms(x, y, SHOPBASE);
2026 struct monst *shkp = shop_keeper(*o_shop);
2027
2028 if (shkp) { /* (implies *o_shop != '\0') */
2029 static NEARDATA long lastmovetime = 0L;
2030 static NEARDATA boolean peaceful_shk = FALSE;
2031 /* We want to base shk actions on her peacefulness
2032 at start of this turn, so that "simultaneous"
2033 multiple breakage isn't drastically worse than
2034 single breakage. (ought to be done via ESHK) */
2035 if (moves != lastmovetime)
2036 peaceful_shk = shkp->mpeaceful;
2037 if (stolen_value(obj, x, y, peaceful_shk, FALSE) > 0L
2038 && (*o_shop != u.ushops[0] || !inside_shop(u.ux, u.uy))
2039 && moves != lastmovetime)
2040 make_angry_shk(shkp, x, y);
2041 lastmovetime = moves;
2042 }
2043 }
2044 }
2045 if (!fracture)
2046 delobj(obj);
2047 }
2048
2049 /*
2050 * Check to see if obj is going to break, but don't actually break it.
2051 * Return 0 if the object isn't going to break, 1 if it is.
2052 */
2053 boolean
breaktest(obj)2054 breaktest(obj)
2055 struct obj *obj;
2056 {
2057 if (obj_resists(obj, 1, 99))
2058 return 0;
2059 if (objects[obj->otyp].oc_material == GLASS && !obj->oartifact
2060 && obj->oclass != GEM_CLASS)
2061 return 1;
2062 switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) {
2063 case EXPENSIVE_CAMERA:
2064 case POT_WATER: /* really, all potions */
2065 case EGG:
2066 case CREAM_PIE:
2067 case MELON:
2068 case ACID_VENOM:
2069 case BLINDING_VENOM:
2070 return 1;
2071 default:
2072 return 0;
2073 }
2074 }
2075
2076 STATIC_OVL void
breakmsg(obj,in_view)2077 breakmsg(obj, in_view)
2078 struct obj *obj;
2079 boolean in_view;
2080 {
2081 const char *to_pieces;
2082
2083 to_pieces = "";
2084 switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) {
2085 default: /* glass or crystal wand */
2086 if (obj->oclass != WAND_CLASS)
2087 impossible("breaking odd object?");
2088 /*FALLTHRU*/
2089 case CRYSTAL_PLATE_MAIL:
2090 case LENSES:
2091 case MIRROR:
2092 case CRYSTAL_BALL:
2093 case EXPENSIVE_CAMERA:
2094 to_pieces = " into a thousand pieces";
2095 /*FALLTHRU*/
2096 case POT_WATER: /* really, all potions */
2097 if (!in_view)
2098 You_hear("%s shatter!", something);
2099 else
2100 pline("%s shatter%s%s!", Doname2(obj),
2101 (obj->quan == 1L) ? "s" : "", to_pieces);
2102 break;
2103 case EGG:
2104 case MELON:
2105 pline("Splat!");
2106 break;
2107 case CREAM_PIE:
2108 if (in_view)
2109 pline("What a mess!");
2110 break;
2111 case ACID_VENOM:
2112 case BLINDING_VENOM:
2113 pline("Splash!");
2114 break;
2115 }
2116 }
2117
2118 STATIC_OVL int
throw_gold(obj)2119 throw_gold(obj)
2120 struct obj *obj;
2121 {
2122 int range, odx, ody;
2123 register struct monst *mon;
2124
2125 if (!u.dx && !u.dy && !u.dz) {
2126 You("cannot throw gold at yourself.");
2127 return 0;
2128 }
2129 freeinv(obj);
2130 if (u.uswallow) {
2131 pline(is_animal(u.ustuck->data) ? "%s in the %s's entrails."
2132 : "%s into %s.",
2133 "The money disappears", mon_nam(u.ustuck));
2134 add_to_minv(u.ustuck, obj);
2135 return 1;
2136 }
2137
2138 if (u.dz) {
2139 if (u.dz < 0 && !Is_airlevel(&u.uz) && !Underwater
2140 && !Is_waterlevel(&u.uz)) {
2141 pline_The("gold hits the %s, then falls back on top of your %s.",
2142 ceiling(u.ux, u.uy), body_part(HEAD));
2143 /* some self damage? */
2144 if (uarmh)
2145 pline("Fortunately, you are wearing %s!",
2146 an(helm_simple_name(uarmh)));
2147 }
2148 bhitpos.x = u.ux;
2149 bhitpos.y = u.uy;
2150 } else {
2151 /* consistent with range for normal objects */
2152 range = (int) ((ACURRSTR) / 2 - obj->owt / 40);
2153
2154 /* see if the gold has a place to move into */
2155 odx = u.ux + u.dx;
2156 ody = u.uy + u.dy;
2157 if (!ZAP_POS(levl[odx][ody].typ) || closed_door(odx, ody)) {
2158 bhitpos.x = u.ux;
2159 bhitpos.y = u.uy;
2160 } else {
2161 mon = bhit(u.dx, u.dy, range, THROWN_WEAPON,
2162 (int FDECL((*), (MONST_P, OBJ_P))) 0,
2163 (int FDECL((*), (OBJ_P, OBJ_P))) 0, &obj);
2164 if (!obj)
2165 return 1; /* object is gone */
2166 if (mon) {
2167 if (ghitm(mon, obj)) /* was it caught? */
2168 return 1;
2169 } else {
2170 if (ship_object(obj, bhitpos.x, bhitpos.y, FALSE))
2171 return 1;
2172 }
2173 }
2174 }
2175
2176 if (flooreffects(obj, bhitpos.x, bhitpos.y, "fall"))
2177 return 1;
2178 if (u.dz > 0)
2179 pline_The("gold hits the %s.", surface(bhitpos.x, bhitpos.y));
2180 place_object(obj, bhitpos.x, bhitpos.y);
2181 if (*u.ushops)
2182 sellobj(obj, bhitpos.x, bhitpos.y);
2183 stackobj(obj);
2184 newsym(bhitpos.x, bhitpos.y);
2185 return 1;
2186 }
2187
2188 /*dothrow.c*/
2189