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