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