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