Iron bars, water and lava are not interesting for travel
[aNetHack.git] / src / ball.c
blob3b81a8f7482ebc6a382ae67767a7ba608d6b1cd9
1 /* NetHack 3.6 ball.c $NHDT-Date: 1450402033 2015/12/18 01:27:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.29 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* Ball & Chain
6 * =============================================================*/
8 #include "hack.h"
10 STATIC_DCL int NDECL(bc_order);
11 STATIC_DCL void NDECL(litter);
13 void
14 ballrelease(showmsg)
15 boolean showmsg;
17 if (carried(uball)) {
18 if (showmsg)
19 pline("Startled, you drop the iron ball.");
20 if (uwep == uball)
21 setuwep((struct obj *) 0);
22 if (uswapwep == uball)
23 setuswapwep((struct obj *) 0);
24 if (uquiver == uball)
25 setuqwep((struct obj *) 0);
27 if (uwep != uball)
28 freeinv(uball);
32 void
33 ballfall()
35 boolean gets_hit;
37 gets_hit = (((uball->ox != u.ux) || (uball->oy != u.uy))
38 && ((uwep == uball) ? FALSE : (boolean) rn2(5)));
39 ballrelease(TRUE);
40 if (gets_hit) {
41 int dmg = rn1(7, 25);
42 pline_The("iron ball falls on your %s.", body_part(HEAD));
43 if (uarmh) {
44 if (is_metallic(uarmh)) {
45 pline("Fortunately, you are wearing a hard helmet.");
46 dmg = 3;
47 } else if (flags.verbose)
48 pline("%s does not protect you.", Yname2(uarmh));
50 losehp(Maybe_Half_Phys(dmg), "crunched in the head by an iron ball",
51 NO_KILLER_PREFIX);
56 * To make this work, we have to mess with the hero's mind. The rules for
57 * ball&chain are:
59 * 1. If the hero can see them, fine.
60 * 2. If the hero can't see either, it isn't seen.
61 * 3. If either is felt it is seen.
62 * 4. If either is felt and moved, it disappears.
64 * If the hero can see, then when a move is done, the ball and chain are
65 * first picked up, the positions under them are corrected, then they
66 * are moved after the hero moves. Not too bad.
68 * If the hero is blind, then she can "feel" the ball and/or chain at any
69 * time. However, when the hero moves, the felt ball and/or chain become
70 * unfelt and whatever was felt "under" the ball&chain appears. Pretty
71 * nifty, but it requires that the ball&chain "remember" what was under
72 * them --- i.e. they pick-up glyphs when they are felt and drop them when
73 * moved (and felt). When swallowed, the ball&chain are pulled completely
74 * off of the dungeon, but are still on the object chain. They are placed
75 * under the hero when she is expelled.
79 * from you.h
80 * int u.bglyph glyph under the ball
81 * int u.cglyph glyph under the chain
82 * int u.bc_felt mask for ball/chain being felt
83 * #define BC_BALL 0x01 bit mask in u.bc_felt for ball
84 * #define BC_CHAIN 0x02 bit mask in u.bc_felt for chain
85 * int u.bc_order ball & chain order
87 * u.bc_felt is also manipulated in display.c and read.c, the others only
88 * in this file. None of these variables are valid unless the player is
89 * Blind.
92 /* values for u.bc_order */
93 #define BCPOS_DIFFER 0 /* ball & chain at different positions */
94 #define BCPOS_CHAIN 1 /* chain on top of ball */
95 #define BCPOS_BALL 2 /* ball on top of chain */
98 * Place the ball & chain under the hero. Make sure that the ball & chain
99 * variables are set (actually only needed when blind, but what the heck).
100 * It is assumed that when this is called, the ball and chain are NOT
101 * attached to the object list.
103 * Should not be called while swallowed except on waterlevel.
105 void
106 placebc()
108 if (!uchain || !uball) {
109 impossible("Where are your ball and chain?");
110 return;
113 (void) flooreffects(uchain, u.ux, u.uy, ""); /* chain might rust */
115 if (carried(uball)) /* the ball is carried */
116 u.bc_order = BCPOS_DIFFER;
117 else {
118 /* ball might rust -- already checked when carried */
119 (void) flooreffects(uball, u.ux, u.uy, "");
120 place_object(uball, u.ux, u.uy);
121 u.bc_order = BCPOS_CHAIN;
124 place_object(uchain, u.ux, u.uy);
126 u.bglyph = u.cglyph = levl[u.ux][u.uy].glyph; /* pick up glyph */
128 newsym(u.ux, u.uy);
131 void
132 unplacebc()
134 if (u.uswallow) {
135 if (Is_waterlevel(&u.uz)) {
136 /* we need to proceed with the removal from the floor
137 * so that movebubbles() processing will disregard it as
138 * intended. Ignore all the vision stuff.
140 if (!carried(uball))
141 obj_extract_self(uball);
142 obj_extract_self(uchain);
144 /* ball&chain not unplaced while swallowed */
145 return;
148 if (!carried(uball)) {
149 obj_extract_self(uball);
150 if (Blind && (u.bc_felt & BC_BALL)) /* drop glyph */
151 levl[uball->ox][uball->oy].glyph = u.bglyph;
153 newsym(uball->ox, uball->oy);
155 obj_extract_self(uchain);
156 if (Blind && (u.bc_felt & BC_CHAIN)) /* drop glyph */
157 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
159 newsym(uchain->ox, uchain->oy);
160 u.bc_felt = 0; /* feel nothing */
164 * Return the stacking of the hero's ball & chain. This assumes that the
165 * hero is being punished.
167 STATIC_OVL int
168 bc_order()
170 struct obj *obj;
172 if (uchain->ox != uball->ox || uchain->oy != uball->oy || carried(uball)
173 || u.uswallow)
174 return BCPOS_DIFFER;
176 for (obj = level.objects[uball->ox][uball->oy]; obj;
177 obj = obj->nexthere) {
178 if (obj == uchain)
179 return BCPOS_CHAIN;
180 if (obj == uball)
181 return BCPOS_BALL;
183 impossible("bc_order: ball&chain not in same location!");
184 return BCPOS_DIFFER;
188 * set_bc()
190 * The hero is either about to go blind or already blind and just punished.
191 * Set up the ball and chain variables so that the ball and chain are "felt".
193 void
194 set_bc(already_blind)
195 int already_blind;
197 int ball_on_floor = !carried(uball);
199 u.bc_order = bc_order(); /* get the order */
200 u.bc_felt = ball_on_floor ? BC_BALL | BC_CHAIN : BC_CHAIN; /* felt */
202 if (already_blind || u.uswallow) {
203 u.cglyph = u.bglyph = levl[u.ux][u.uy].glyph;
204 return;
208 * Since we can still see, remove the ball&chain and get the glyph that
209 * would be beneath them. Then put the ball&chain back. This is pretty
210 * disgusting, but it will work.
212 remove_object(uchain);
213 if (ball_on_floor)
214 remove_object(uball);
216 newsym(uchain->ox, uchain->oy);
217 u.cglyph = levl[uchain->ox][uchain->oy].glyph;
219 if (u.bc_order == BCPOS_DIFFER) { /* different locations */
220 place_object(uchain, uchain->ox, uchain->oy);
221 newsym(uchain->ox, uchain->oy);
222 if (ball_on_floor) {
223 newsym(uball->ox, uball->oy); /* see under ball */
224 u.bglyph = levl[uball->ox][uball->oy].glyph;
225 place_object(uball, uball->ox, uball->oy);
226 newsym(uball->ox, uball->oy); /* restore ball */
228 } else {
229 u.bglyph = u.cglyph;
230 if (u.bc_order == BCPOS_CHAIN) {
231 place_object(uball, uball->ox, uball->oy);
232 place_object(uchain, uchain->ox, uchain->oy);
233 } else {
234 place_object(uchain, uchain->ox, uchain->oy);
235 place_object(uball, uball->ox, uball->oy);
237 newsym(uball->ox, uball->oy);
242 * move_bc()
244 * Move the ball and chain. This is called twice for every move. The first
245 * time to pick up the ball and chain before the move, the second time to
246 * place the ball and chain after the move. If the ball is carried, this
247 * function should never have BC_BALL as part of its control.
249 * Should not be called while swallowed.
251 void
252 move_bc(before, control, ballx, bally, chainx, chainy)
253 int before, control;
254 xchar ballx, bally, chainx, chainy; /* only matter !before */
256 if (Blind) {
258 * The hero is blind. Time to work hard. The ball and chain that
259 * are attached to the hero are very special. The hero knows that
260 * they are attached, so when they move, the hero knows that they
261 * aren't at the last position remembered. This is complicated
262 * by the fact that the hero can "feel" the surrounding locations
263 * at any time, hence, making one or both of them show up again.
264 * So, we have to keep track of which is felt at any one time and
265 * act accordingly.
267 if (!before) {
268 if ((control & BC_CHAIN) && (control & BC_BALL)) {
270 * Both ball and chain moved. If felt, drop glyph.
272 if (u.bc_felt & BC_BALL)
273 levl[uball->ox][uball->oy].glyph = u.bglyph;
274 if (u.bc_felt & BC_CHAIN)
275 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
276 u.bc_felt = 0;
278 /* Pick up glyph at new location. */
279 u.bglyph = levl[ballx][bally].glyph;
280 u.cglyph = levl[chainx][chainy].glyph;
282 movobj(uball, ballx, bally);
283 movobj(uchain, chainx, chainy);
284 } else if (control & BC_BALL) {
285 if (u.bc_felt & BC_BALL) {
286 if (u.bc_order == BCPOS_DIFFER) { /* ball by itself */
287 levl[uball->ox][uball->oy].glyph = u.bglyph;
288 } else if (u.bc_order == BCPOS_BALL) {
289 if (u.bc_felt & BC_CHAIN) { /* know chain is there */
290 map_object(uchain, 0);
291 } else {
292 levl[uball->ox][uball->oy].glyph = u.bglyph;
295 u.bc_felt &= ~BC_BALL; /* no longer feel the ball */
298 /* Pick up glyph at new position. */
299 u.bglyph = (ballx != chainx || bally != chainy)
300 ? levl[ballx][bally].glyph
301 : u.cglyph;
303 movobj(uball, ballx, bally);
304 } else if (control & BC_CHAIN) {
305 if (u.bc_felt & BC_CHAIN) {
306 if (u.bc_order == BCPOS_DIFFER) {
307 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
308 } else if (u.bc_order == BCPOS_CHAIN) {
309 if (u.bc_felt & BC_BALL) {
310 map_object(uball, 0);
311 } else {
312 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
315 u.bc_felt &= ~BC_CHAIN;
317 /* Pick up glyph at new position. */
318 u.cglyph = (ballx != chainx || bally != chainy)
319 ? levl[chainx][chainy].glyph
320 : u.bglyph;
322 movobj(uchain, chainx, chainy);
325 u.bc_order = bc_order(); /* reset the order */
328 } else {
330 * The hero is not blind. To make this work correctly, we need to
331 * pick up the ball and chain before the hero moves, then put them
332 * in their new positions after the hero moves.
334 if (before) {
335 if (!control) {
337 * Neither ball nor chain is moving, so remember which was
338 * on top until !before. Use the variable u.bc_order
339 * since it is only valid when blind.
341 u.bc_order = bc_order();
344 remove_object(uchain);
345 newsym(uchain->ox, uchain->oy);
346 if (!carried(uball)) {
347 remove_object(uball);
348 newsym(uball->ox, uball->oy);
350 } else {
351 int on_floor = !carried(uball);
353 if ((control & BC_CHAIN)
354 || (!control && u.bc_order == BCPOS_CHAIN)) {
355 /* If the chain moved or nothing moved & chain on top. */
356 if (on_floor)
357 place_object(uball, ballx, bally);
358 place_object(uchain, chainx, chainy); /* chain on top */
359 } else {
360 place_object(uchain, chainx, chainy);
361 if (on_floor)
362 place_object(uball, ballx, bally);
363 /* ball on top */
365 newsym(chainx, chainy);
366 if (on_floor)
367 newsym(ballx, bally);
372 /* return TRUE if the caller needs to place the ball and chain down again
374 * Should not be called while swallowed. Should be called before movement,
375 * because we might want to move the ball or chain to the hero's old
376 * position.
378 * It is called if we are moving. It is also called if we are teleporting
379 * *if* the ball doesn't move and we thus must drag the chain. It is not
380 * called for ordinary teleportation.
382 * allow_drag is only used in the ugly special case where teleporting must
383 * drag the chain, while an identical-looking movement must drag both the ball
384 * and chain.
386 boolean
387 drag_ball(x, y, bc_control, ballx, bally, chainx, chainy, cause_delay,
388 allow_drag)
389 xchar x, y;
390 int *bc_control;
391 xchar *ballx, *bally, *chainx, *chainy;
392 boolean *cause_delay;
393 boolean allow_drag;
395 struct trap *t = (struct trap *) 0;
396 boolean already_in_rock;
398 *ballx = uball->ox;
399 *bally = uball->oy;
400 *chainx = uchain->ox;
401 *chainy = uchain->oy;
402 *bc_control = 0;
403 *cause_delay = FALSE;
405 if (dist2(x, y, uchain->ox, uchain->oy) <= 2) { /* nothing moved */
406 move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
407 return TRUE;
410 /* only need to move the chain? */
411 if (carried(uball) || distmin(x, y, uball->ox, uball->oy) <= 2) {
412 xchar oldchainx = uchain->ox, oldchainy = uchain->oy;
413 *bc_control = BC_CHAIN;
414 move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
415 if (carried(uball)) {
416 /* move chain only if necessary */
417 if (distmin(x, y, uchain->ox, uchain->oy) > 1) {
418 *chainx = u.ux;
419 *chainy = u.uy;
421 return TRUE;
423 #define CHAIN_IN_MIDDLE(chx, chy) \
424 (distmin(x, y, chx, chy) <= 1 \
425 && distmin(chx, chy, uball->ox, uball->oy) <= 1)
426 #define IS_CHAIN_ROCK(x, y) \
427 (IS_ROCK(levl[x][y].typ) \
428 || (IS_DOOR(levl[x][y].typ) \
429 && (levl[x][y].doormask & (D_CLOSED | D_LOCKED))))
430 /* Don't ever move the chain into solid rock. If we have to, then instead
431 * undo the move_bc() and jump to the drag ball code. Note that this also
432 * means the "cannot carry and drag" message will not appear, since unless we
433 * moved at least two squares there is no possibility of the chain position
434 * being in solid rock.
436 #define SKIP_TO_DRAG \
438 *chainx = oldchainx; \
439 *chainy = oldchainy; \
440 move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \
441 goto drag; \
443 if (IS_CHAIN_ROCK(u.ux, u.uy) || IS_CHAIN_ROCK(*chainx, *chainy)
444 || IS_CHAIN_ROCK(uball->ox, uball->oy))
445 already_in_rock = TRUE;
446 else
447 already_in_rock = FALSE;
449 switch (dist2(x, y, uball->ox, uball->oy)) {
450 /* two spaces diagonal from ball, move chain inbetween */
451 case 8:
452 *chainx = (uball->ox + x) / 2;
453 *chainy = (uball->oy + y) / 2;
454 if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
455 SKIP_TO_DRAG;
456 break;
458 /* player is distance 2/1 from ball; move chain to one of the
459 * two spaces between
461 * __
464 case 5: {
465 xchar tempx, tempy, tempx2, tempy2;
467 /* find position closest to current position of chain */
468 /* no effect if current position is already OK */
469 if (abs(x - uball->ox) == 1) {
470 tempx = x;
471 tempx2 = uball->ox;
472 tempy = tempy2 = (uball->oy + y) / 2;
473 } else {
474 tempx = tempx2 = (uball->ox + x) / 2;
475 tempy = y;
476 tempy2 = uball->oy;
478 if (IS_CHAIN_ROCK(tempx, tempy) && !IS_CHAIN_ROCK(tempx2, tempy2)
479 && !already_in_rock) {
480 if (allow_drag) {
481 /* Avoid pathological case *if* not teleporting:
482 * 0 0_
483 * _X move northeast -----> X@
486 if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5
487 && dist2(x, y, tempx, tempy) == 1)
488 SKIP_TO_DRAG;
489 /* Avoid pathological case *if* not teleporting:
490 * 0 0
491 * _X move east -----> X_
492 * @ @
494 if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4
495 && dist2(x, y, tempx, tempy) == 2)
496 SKIP_TO_DRAG;
498 *chainx = tempx2;
499 *chainy = tempy2;
500 } else if (!IS_CHAIN_ROCK(tempx, tempy)
501 && IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
502 if (allow_drag) {
503 if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5
504 && dist2(x, y, tempx2, tempy2) == 1)
505 SKIP_TO_DRAG;
506 if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4
507 && dist2(x, y, tempx2, tempy2) == 2)
508 SKIP_TO_DRAG;
510 *chainx = tempx;
511 *chainy = tempy;
512 } else if (IS_CHAIN_ROCK(tempx, tempy)
513 && IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
514 SKIP_TO_DRAG;
515 } else if (dist2(tempx, tempy, uchain->ox, uchain->oy)
516 < dist2(tempx2, tempy2, uchain->ox, uchain->oy)
517 || ((dist2(tempx, tempy, uchain->ox, uchain->oy)
518 == dist2(tempx2, tempy2, uchain->ox, uchain->oy))
519 && rn2(2))) {
520 *chainx = tempx;
521 *chainy = tempy;
522 } else {
523 *chainx = tempx2;
524 *chainy = tempy2;
526 break;
529 /* ball is two spaces horizontal or vertical from player; move*/
530 /* chain inbetween *unless* current chain position is OK */
531 case 4:
532 if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
533 break;
534 *chainx = (x + uball->ox) / 2;
535 *chainy = (y + uball->oy) / 2;
536 if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
537 SKIP_TO_DRAG;
538 break;
540 /* ball is one space diagonal from player. Check for the
541 * following special case:
543 * _ moving southwest becomes @_
544 * 0 0
545 * (This will also catch teleporting that happens to resemble
546 * this case, but oh well.) Otherwise fall through.
548 case 2:
549 if (dist2(x, y, uball->ox, uball->oy) == 2
550 && dist2(x, y, uchain->ox, uchain->oy) == 4) {
551 if (uchain->oy == y)
552 *chainx = uball->ox;
553 else
554 *chainy = uball->oy;
555 if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
556 SKIP_TO_DRAG;
557 break;
559 /* fall through */
560 case 1:
561 case 0:
562 /* do nothing if possible */
563 if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
564 break;
565 /* otherwise try to drag chain to player's old position */
566 if (CHAIN_IN_MIDDLE(u.ux, u.uy)) {
567 *chainx = u.ux;
568 *chainy = u.uy;
569 break;
571 /* otherwise use player's new position (they must have
572 teleported, for this to happen) */
573 *chainx = x;
574 *chainy = y;
575 break;
577 default:
578 impossible("bad chain movement");
579 break;
581 #undef SKIP_TO_DRAG
582 #undef CHAIN_IN_MIDDLE
583 return TRUE;
586 drag:
588 if (near_capacity() > SLT_ENCUMBER && dist2(x, y, u.ux, u.uy) <= 2) {
589 You("cannot %sdrag the heavy iron ball.",
590 invent ? "carry all that and also " : "");
591 nomul(0);
592 return FALSE;
595 if ((is_pool(uchain->ox, uchain->oy)
596 /* water not mere continuation of previous water */
597 && (levl[uchain->ox][uchain->oy].typ == POOL
598 || !is_pool(uball->ox, uball->oy)
599 || levl[uball->ox][uball->oy].typ == POOL))
600 || ((t = t_at(uchain->ox, uchain->oy))
601 && (t->ttyp == PIT || t->ttyp == SPIKED_PIT || t->ttyp == HOLE
602 || t->ttyp == TRAPDOOR))) {
603 if (Levitation) {
604 You_feel("a tug from the iron ball.");
605 if (t)
606 t->tseen = 1;
607 } else {
608 struct monst *victim;
610 You("are jerked back by the iron ball!");
611 if ((victim = m_at(uchain->ox, uchain->oy)) != 0) {
612 int tmp;
614 tmp = -2 + Luck + find_mac(victim);
615 tmp += omon_adj(victim, uball, TRUE);
616 if (tmp >= rnd(20))
617 (void) hmon(victim, uball, HMON_DRAGGED);
618 else
619 miss(xname(uball), victim);
621 } /* now check again in case mon died */
622 if (!m_at(uchain->ox, uchain->oy)) {
623 u.ux = uchain->ox;
624 u.uy = uchain->oy;
625 newsym(u.ux0, u.uy0);
627 nomul(0);
629 *bc_control = BC_BALL;
630 move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
631 *ballx = uchain->ox;
632 *bally = uchain->oy;
633 move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy);
634 spoteffects(TRUE);
635 return FALSE;
639 *bc_control = BC_BALL | BC_CHAIN;
641 move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
642 if (dist2(x, y, u.ux, u.uy) > 2) {
643 /* Awful case: we're still in range of the ball, so we thought we
644 * could only move the chain, but it turned out that the target
645 * square for the chain was rock, so we had to drag it instead.
646 * But we can't drag it either, because we teleported and are more
647 * than one square from our old position. Revert to the teleport
648 * behavior.
650 *ballx = *chainx = x;
651 *bally = *chainy = y;
652 } else {
653 xchar newchainx = u.ux, newchainy = u.uy;
656 * Generally, chain moves to hero's previous location and ball
657 * moves to chain's previous location, except that we try to
658 * keep the chain directly between the hero and the ball. But,
659 * take the simple approach if the hero's previous location or
660 * the potential between location is inaccessible.
662 if (dist2(x, y, uchain->ox, uchain->oy) == 4
663 && !IS_CHAIN_ROCK(newchainx, newchainy)) {
664 newchainx = (x + uchain->ox) / 2;
665 newchainy = (y + uchain->oy) / 2;
666 if (IS_CHAIN_ROCK(newchainx, newchainy)) {
667 /* don't let chain move to inaccessible location */
668 newchainx = u.ux;
669 newchainy = u.uy;
673 *ballx = uchain->ox;
674 *bally = uchain->oy;
675 *chainx = newchainx;
676 *chainy = newchainy;
678 #undef IS_CHAIN_ROCK
679 *cause_delay = TRUE;
680 return TRUE;
684 * drop_ball()
686 * The punished hero drops or throws her iron ball. If the hero is
687 * blind, we must reset the order and glyph. Check for side effects.
688 * This routine expects the ball to be already placed.
690 * Should not be called while swallowed.
692 void
693 drop_ball(x, y)
694 xchar x, y;
696 if (Blind) {
697 /* get the order */
698 u.bc_order = bc_order();
699 /* pick up glyph */
700 u.bglyph = (u.bc_order) ? u.cglyph : levl[x][y].glyph;
703 if (x != u.ux || y != u.uy) {
704 struct trap *t;
705 const char *pullmsg = "The ball pulls you out of the %s!";
707 if (u.utrap && u.utraptype != TT_INFLOOR
708 && u.utraptype != TT_BURIEDBALL) {
709 switch (u.utraptype) {
710 case TT_PIT:
711 pline(pullmsg, "pit");
712 break;
713 case TT_WEB:
714 pline(pullmsg, "web");
715 pline_The("web is destroyed!");
716 deltrap(t_at(u.ux, u.uy));
717 break;
718 case TT_LAVA:
719 pline(pullmsg, hliquid("lava"));
720 break;
721 case TT_BEARTRAP: {
722 register long side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
723 pline(pullmsg, "bear trap");
724 set_wounded_legs(side, rn1(1000, 500));
725 if (!u.usteed) {
726 Your("%s %s is severely damaged.",
727 (side == LEFT_SIDE) ? "left" : "right",
728 body_part(LEG));
729 losehp(Maybe_Half_Phys(2),
730 "leg damage from being pulled out of a bear trap",
731 KILLED_BY);
733 break;
736 u.utrap = 0;
737 fill_pit(u.ux, u.uy);
740 u.ux0 = u.ux;
741 u.uy0 = u.uy;
742 if (!Levitation && !MON_AT(x, y) && !u.utrap
743 && (is_pool(x, y)
744 || ((t = t_at(x, y))
745 && (t->ttyp == PIT || t->ttyp == SPIKED_PIT
746 || t->ttyp == TRAPDOOR || t->ttyp == HOLE)))) {
747 u.ux = x;
748 u.uy = y;
749 } else {
750 u.ux = x - u.dx;
751 u.uy = y - u.dy;
753 vision_full_recalc = 1; /* hero has moved, recalculate vision later */
755 if (Blind) {
756 /* drop glyph under the chain */
757 if (u.bc_felt & BC_CHAIN)
758 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
759 u.bc_felt = 0; /* feel nothing */
760 /* pick up new glyph */
761 u.cglyph = (u.bc_order) ? u.bglyph : levl[u.ux][u.uy].glyph;
763 movobj(uchain, u.ux, u.uy); /* has a newsym */
764 if (Blind) {
765 u.bc_order = bc_order();
767 newsym(u.ux0, u.uy0); /* clean up old position */
768 if (u.ux0 != u.ux || u.uy0 != u.uy) {
769 spoteffects(TRUE);
770 sokoban_guilt();
775 STATIC_OVL void
776 litter()
778 struct obj *otmp = invent, *nextobj;
779 int capacity = weight_cap();
781 while (otmp) {
782 nextobj = otmp->nobj;
783 if ((otmp != uball) && (rnd(capacity) <= (int) otmp->owt)) {
784 if (canletgo(otmp, "")) {
785 pline("%s you down the stairs.", Yobjnam2(otmp, "follow"));
786 dropx(otmp);
789 otmp = nextobj;
793 void
794 drag_down()
796 boolean forward;
797 uchar dragchance = 3;
800 * Assume that the ball falls forward if:
802 * a) the character is wielding it, or
803 * b) the character has both hands available to hold it (i.e. is
804 * not wielding any weapon), or
805 * c) (perhaps) it falls forward out of his non-weapon hand
807 forward = carried(uball) && (uwep == uball || !uwep || !rn2(3));
809 if (carried(uball))
810 You("lose your grip on the iron ball.");
812 cls(); /* previous level is still displayed although you
813 went down the stairs. Avoids bug C343-20 */
815 if (forward) {
816 if (rn2(6)) {
817 pline_The("iron ball drags you downstairs!");
818 losehp(Maybe_Half_Phys(rnd(6)),
819 "dragged downstairs by an iron ball", NO_KILLER_PREFIX);
820 litter();
822 } else {
823 if (rn2(2)) {
824 pline_The("iron ball smacks into you!");
825 losehp(Maybe_Half_Phys(rnd(20)), "iron ball collision",
826 KILLED_BY_AN);
827 exercise(A_STR, FALSE);
828 dragchance -= 2;
830 if ((int) dragchance >= rnd(6)) {
831 pline_The("iron ball drags you downstairs!");
832 losehp(Maybe_Half_Phys(rnd(3)),
833 "dragged downstairs by an iron ball", NO_KILLER_PREFIX);
834 exercise(A_STR, FALSE);
835 litter();
840 /*ball.c*/