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