1 /* NetHack 3.7 ball.c $NHDT-Date: 1596498150 2020/08/03 23:42:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.51 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) David Cohrs, 2006. */
4 /* NetHack may be freely redistributed. See license for details. */
7 * =============================================================*/
11 staticfn
int bc_order(void);
12 staticfn
void litter(void);
13 staticfn
void placebc_core(void);
14 staticfn
void unplacebc_core(void);
15 staticfn boolean
check_restriction(int);
17 static int bcrestriction
= 0;
19 static struct breadcrumbs bcpbreadcrumbs
= {0}, bcubreadcrumbs
= {0};
23 ballrelease(boolean showmsg
)
25 if (carried(uball
) && !welded(uball
)) {
27 pline("Startled, you drop the iron ball.");
29 setuwep((struct obj
*) 0);
30 if (uswapwep
== uball
)
31 setuswapwep((struct obj
*) 0);
33 setuqwep((struct obj
*) 0);
34 /* [this used to test 'if (uwep != uball)' but that always passes
35 after the setuwep() above] */
36 freeinv(uball
); /* remove from inventory but don't place on floor */
37 (void) encumber_msg();
41 /* ball&chain might hit hero when falling through a trap door */
47 if (!uball
|| (uball
&& carried(uball
) && welded(uball
)))
50 gets_hit
= (((uball
->ox
!= u
.ux
) || (uball
->oy
!= u
.uy
))
51 && ((uwep
== uball
) ? FALSE
: (boolean
) rn2(5)));
56 pline_The("iron ball falls on your %s.", body_part(HEAD
));
58 if (hard_helmet(uarmh
)) {
59 pline("Fortunately, you are wearing a hard helmet.");
61 } else if (flags
.verbose
)
62 pline("%s does not protect you.", Yname2(uarmh
));
64 losehp(Maybe_Half_Phys(dmg
), "crunched in the head by an iron ball",
70 * To make this work, we have to mess with the hero's mind. The rules for
73 * 1. If the hero can see them, fine.
74 * 2. If the hero can't see either, it isn't seen.
75 * 3. If either is felt it is seen.
76 * 4. If either is felt and moved, it disappears.
78 * If the hero can see, then when a move is done, the ball and chain are
79 * first picked up, the positions under them are corrected, then they
80 * are moved after the hero moves. Not too bad.
82 * If the hero is blind, then she can "feel" the ball and/or chain at any
83 * time. However, when the hero moves, the felt ball and/or chain become
84 * unfelt and whatever was felt "under" the ball&chain appears. Pretty
85 * nifty, but it requires that the ball&chain "remember" what was under
86 * them --- i.e. they pick-up glyphs when they are felt and drop them when
87 * moved (and felt). When swallowed, the ball&chain are pulled completely
88 * off of the dungeon, but are still on the object chain. They are placed
89 * under the hero when she is expelled.
94 * int u.bglyph glyph under the ball
95 * int u.cglyph glyph under the chain
96 * int u.bc_felt mask for ball/chain being felt
97 * #define BC_BALL 0x01 bit mask in u.bc_felt for ball
98 * #define BC_CHAIN 0x02 bit mask in u.bc_felt for chain
99 * int u.bc_order ball & chain order
101 * u.bc_felt is also manipulated in display.c and read.c, the others only
102 * in this file. None of these variables are valid unless the player is
106 /* values for u.bc_order */
107 #define BCPOS_DIFFER 0 /* ball & chain at different positions */
108 #define BCPOS_CHAIN 1 /* chain on top of ball */
109 #define BCPOS_BALL 2 /* ball on top of chain */
112 * Place the ball & chain under the hero. Make sure that the ball & chain
113 * variables are set (actually only needed when blind, but what the heck).
114 * It is assumed that when this is called, the ball and chain are NOT
115 * attached to the object list.
117 * Should not be called while swallowed except on waterlevel.
122 if (!uchain
|| !uball
) {
123 impossible("Where are your ball and chain?");
127 (void) flooreffects(uchain
, u
.ux
, u
.uy
, ""); /* chain might rust */
129 if (carried(uball
)) { /* the ball is carried */
130 u
.bc_order
= BCPOS_DIFFER
;
132 /* ball might rust -- already checked when carried */
133 (void) flooreffects(uball
, u
.ux
, u
.uy
, "");
134 place_object(uball
, u
.ux
, u
.uy
);
135 u
.bc_order
= BCPOS_CHAIN
;
138 place_object(uchain
, u
.ux
, u
.uy
);
140 u
.bglyph
= u
.cglyph
= levl
[u
.ux
][u
.uy
].glyph
; /* pick up glyph */
150 if (Is_waterlevel(&u
.uz
)) {
151 /* we need to proceed with the removal from the floor
152 * so that movebubbles() processing will disregard it as
153 * intended. Ignore all the vision stuff.
156 obj_extract_self(uball
);
157 obj_extract_self(uchain
);
159 /* ball&chain not unplaced while swallowed */
163 if (!carried(uball
)) {
164 obj_extract_self(uball
);
165 if (Blind
&& (u
.bc_felt
& BC_BALL
)) /* drop glyph */
166 levl
[uball
->ox
][uball
->oy
].glyph
= u
.bglyph
;
167 maybe_unhide_at(uball
->ox
, uball
->oy
);
168 newsym(uball
->ox
, uball
->oy
);
170 obj_extract_self(uchain
);
171 if (Blind
&& (u
.bc_felt
& BC_CHAIN
)) /* drop glyph */
172 levl
[uchain
->ox
][uchain
->oy
].glyph
= u
.cglyph
;
173 maybe_unhide_at(uchain
->ox
, uchain
->oy
);
175 newsym(uchain
->ox
, uchain
->oy
);
176 u
.bc_felt
= 0; /* feel nothing */
180 check_restriction(int restriction
)
184 if (!bcrestriction
|| (restriction
== override_restriction
))
187 ret
= (bcrestriction
== restriction
) ? TRUE
: FALSE
;
195 if (!check_restriction(0)) {
196 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
197 char panicbuf
[BUFSZ
];
199 Sprintf(panicbuf
, "placebc denied, restriction in effect");
200 paniclog("placebc", panicbuf
);
204 if (uchain
&& uchain
->where
!= OBJ_FREE
) {
205 impossible("bc already placed?");
215 impossible("unplacebc denied, restriction in place");
222 unplacebc_and_covet_placebc(void)
227 impossible("unplacebc_and_covet_placebc denied, already restricted");
229 restriction
= bcrestriction
= rnd(400);
236 lift_covet_and_placebc(int pin
)
238 if (!check_restriction(pin
)) {
239 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
240 char panicbuf
[BUFSZ
];
242 Sprintf(panicbuf
, "lift_covet_and_placebc denied, %s",
243 (pin
!= bcrestriction
) ? "pin mismatch"
244 : "restriction in effect");
245 paniclog("placebc", panicbuf
);
249 if (uchain
&& uchain
->where
!= OBJ_FREE
) {
250 impossible("bc already placed?");
256 #else /* BREADCRUMBS */
259 Placebc(const char *funcnm
, int linenum
)
261 if (!check_restriction(0)) {
262 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
263 char panicbuf
[BUFSZ
];
265 Sprintf(panicbuf
, "Placebc denied to %s:%d, restricted by %s:%d",
267 bcpbreadcrumbs
.funcnm
, bcpbreadcrumbs
.linenum
);
268 paniclog("Placebc", panicbuf
);
272 if ((uchain
&& uchain
->where
!= OBJ_FREE
)
273 && bcpbreadcrumbs
.in_effect
) {
274 impossible("Placebc collision at %s:%d, already placed by %s:%d",
276 bcpbreadcrumbs
.funcnm
, bcpbreadcrumbs
.linenum
);
279 bcpbreadcrumbs
.in_effect
= TRUE
;
280 bcubreadcrumbs
.in_effect
= FALSE
;
281 bcpbreadcrumbs
.funcnm
= funcnm
;
282 bcpbreadcrumbs
.linenum
= linenum
;
287 Unplacebc(const char *funcnm
, int linenum
)
291 char panicbuf
[BUFSZ
];
293 Sprintf(panicbuf
, "Unplacebc from %s:%d, when restricted to %s:%d",
295 bcubreadcrumbs
.funcnm
, bcubreadcrumbs
.linenum
);
296 paniclog("Unplacebc", panicbuf
);
298 bcpbreadcrumbs
.in_effect
= FALSE
;
299 bcubreadcrumbs
.in_effect
= TRUE
;
300 bcubreadcrumbs
.funcnm
= funcnm
;
301 bcubreadcrumbs
.linenum
= linenum
;
306 Unplacebc_and_covet_placebc(const char *funcnm
, int linenum
)
312 "Unplacebc_and_covet_placebc denied to %s:%d, restricted by %s:%d",
314 bcubreadcrumbs
.funcnm
, bcubreadcrumbs
.linenum
);
316 restriction
= bcrestriction
= rnd(400);
317 bcpbreadcrumbs
.in_effect
= FALSE
;
318 bcubreadcrumbs
.in_effect
= TRUE
;
319 bcubreadcrumbs
.funcnm
= funcnm
;
320 bcubreadcrumbs
.linenum
= linenum
;
327 Lift_covet_and_placebc(int pin
, char *funcnm
, int linenum
)
329 if (!check_restriction(pin
)) {
330 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
331 char panicbuf
[BUFSZ
];
334 "Lift_covet_and_placebc denied to %s:%d, restricted by %s:%d",
336 bcpbreadcrumbs
.funcnm
, bcpbreadcrumbs
.linenum
);
337 paniclog("Lift_covet_and_placebc", panicbuf
);
341 if (uchain
&& uchain
->where
!= OBJ_FREE
) {
342 impossible("bc already placed?");
347 #endif /* BREADCRUMBS */
350 * Return the stacking of the hero's ball & chain. This assumes that the
351 * hero is being punished.
358 if (uchain
->ox
!= uball
->ox
|| uchain
->oy
!= uball
->oy
|| carried(uball
)
362 for (obj
= svl
.level
.objects
[uball
->ox
][uball
->oy
]; obj
;
363 obj
= obj
->nexthere
) {
369 impossible("bc_order: ball&chain not in same location!");
376 * The hero is either about to go blind or already blind and just punished.
377 * Set up the ball and chain variables so that the ball and chain are "felt".
380 set_bc(int already_blind
)
382 int ball_on_floor
= !carried(uball
);
384 u
.bc_order
= bc_order(); /* get the order */
385 u
.bc_felt
= ball_on_floor
? BC_BALL
| BC_CHAIN
: BC_CHAIN
; /* felt */
387 if (already_blind
|| u
.uswallow
) {
388 u
.cglyph
= u
.bglyph
= levl
[u
.ux
][u
.uy
].glyph
;
393 * Since we can still see, remove the ball&chain and get the glyph that
394 * would be beneath them. Then put the ball&chain back. This is pretty
395 * disgusting, but it will work.
397 remove_object(uchain
);
399 remove_object(uball
);
401 newsym(uchain
->ox
, uchain
->oy
);
402 u
.cglyph
= levl
[uchain
->ox
][uchain
->oy
].glyph
;
404 if (u
.bc_order
== BCPOS_DIFFER
) { /* different locations */
405 place_object(uchain
, uchain
->ox
, uchain
->oy
);
406 newsym(uchain
->ox
, uchain
->oy
);
408 newsym(uball
->ox
, uball
->oy
); /* see under ball */
409 u
.bglyph
= levl
[uball
->ox
][uball
->oy
].glyph
;
410 place_object(uball
, uball
->ox
, uball
->oy
);
411 newsym(uball
->ox
, uball
->oy
); /* restore ball */
415 if (u
.bc_order
== BCPOS_CHAIN
) {
416 place_object(uball
, uball
->ox
, uball
->oy
);
417 place_object(uchain
, uchain
->ox
, uchain
->oy
);
419 place_object(uchain
, uchain
->ox
, uchain
->oy
);
420 place_object(uball
, uball
->ox
, uball
->oy
);
422 newsym(uball
->ox
, uball
->oy
);
429 * Move the ball and chain. This is called twice for every move. The first
430 * time to pick up the ball and chain before the move, the second time to
431 * place the ball and chain after the move. If the ball is carried, this
432 * function should never have BC_BALL as part of its control.
434 * Should not be called while swallowed.
437 move_bc(int before
, int control
, coordxy ballx
, coordxy bally
,
438 coordxy chainx
, coordxy chainy
)
442 * The hero is blind. Time to work hard. The ball and chain that
443 * are attached to the hero are very special. The hero knows that
444 * they are attached, so when they move, the hero knows that they
445 * aren't at the last position remembered. This is complicated
446 * by the fact that the hero can "feel" the surrounding locations
447 * at any time, hence, making one or both of them show up again.
448 * So, we have to keep track of which is felt at any one time and
452 if ((control
& BC_CHAIN
) && (control
& BC_BALL
)) {
454 * Both ball and chain moved. If felt, drop glyph.
456 if (u
.bc_felt
& BC_BALL
)
457 levl
[uball
->ox
][uball
->oy
].glyph
= u
.bglyph
;
458 if (u
.bc_felt
& BC_CHAIN
)
459 levl
[uchain
->ox
][uchain
->oy
].glyph
= u
.cglyph
;
462 /* Pick up glyph at new location. */
463 u
.bglyph
= levl
[ballx
][bally
].glyph
;
464 u
.cglyph
= levl
[chainx
][chainy
].glyph
;
466 movobj(uball
, ballx
, bally
);
467 movobj(uchain
, chainx
, chainy
);
468 } else if (control
& BC_BALL
) {
469 if (u
.bc_felt
& BC_BALL
) {
470 if (u
.bc_order
== BCPOS_DIFFER
) { /* ball by itself */
471 levl
[uball
->ox
][uball
->oy
].glyph
= u
.bglyph
;
472 } else if (u
.bc_order
== BCPOS_BALL
) {
473 if (u
.bc_felt
& BC_CHAIN
) { /* know chain is there */
474 map_object(uchain
, 0);
476 levl
[uball
->ox
][uball
->oy
].glyph
= u
.bglyph
;
479 u
.bc_felt
&= ~BC_BALL
; /* no longer feel the ball */
482 /* Pick up glyph at new position. */
483 u
.bglyph
= (ballx
!= chainx
|| bally
!= chainy
)
484 ? levl
[ballx
][bally
].glyph
487 movobj(uball
, ballx
, bally
);
488 } else if (control
& BC_CHAIN
) {
489 if (u
.bc_felt
& BC_CHAIN
) {
490 if (u
.bc_order
== BCPOS_DIFFER
) {
491 levl
[uchain
->ox
][uchain
->oy
].glyph
= u
.cglyph
;
492 } else if (u
.bc_order
== BCPOS_CHAIN
) {
493 if (u
.bc_felt
& BC_BALL
) {
494 map_object(uball
, 0);
496 levl
[uchain
->ox
][uchain
->oy
].glyph
= u
.cglyph
;
499 u
.bc_felt
&= ~BC_CHAIN
;
501 /* Pick up glyph at new position. */
502 u
.cglyph
= (ballx
!= chainx
|| bally
!= chainy
)
503 ? levl
[chainx
][chainy
].glyph
506 movobj(uchain
, chainx
, chainy
);
509 u
.bc_order
= bc_order(); /* reset the order */
514 * The hero is not blind. To make this work correctly, we need to
515 * pick up the ball and chain before the hero moves, then put them
516 * in their new positions after the hero moves.
521 * Neither ball nor chain is moving, so remember which was
522 * on top until !before. Use the variable u.bc_order
523 * since it is only valid when blind.
525 u
.bc_order
= bc_order();
528 remove_object(uchain
);
529 maybe_unhide_at(uchain
->ox
, uchain
->oy
);
530 newsym(uchain
->ox
, uchain
->oy
);
531 if (!carried(uball
)) {
532 remove_object(uball
);
533 maybe_unhide_at(uball
->ox
, uball
->oy
);
534 newsym(uball
->ox
, uball
->oy
);
537 int on_floor
= !carried(uball
);
539 if ((control
& BC_CHAIN
)
540 || (!control
&& u
.bc_order
== BCPOS_CHAIN
)) {
541 /* If the chain moved or nothing moved & chain on top. */
543 place_object(uball
, ballx
, bally
);
544 place_object(uchain
, chainx
, chainy
); /* chain on top */
546 place_object(uchain
, chainx
, chainy
);
548 place_object(uball
, ballx
, bally
);
551 newsym(chainx
, chainy
);
553 newsym(ballx
, bally
);
558 /* return TRUE if the caller needs to place the ball and chain down again */
560 drag_ball(coordxy x
, coordxy y
, int *bc_control
,
561 coordxy
*ballx
, coordxy
*bally
, coordxy
*chainx
, coordxy
*chainy
,
562 boolean
*cause_delay
, boolean allow_drag
)
564 struct trap
*t
= (struct trap
*) 0;
565 boolean already_in_rock
;
568 * Should not be called while swallowed. Should be called before
569 * movement, because we might want to move the ball or chain to the
570 * hero's old position.
572 * It is called if we are moving. It is also called if we are
573 * teleporting *if* the ball doesn't move and we thus must drag the
574 * chain. It is not called for ordinary teleportation.
576 * 'allow_drag' is only used in the ugly special case where teleporting
577 * must drag the chain, while an identical-looking movement must drag
578 * both the ball and chain.
583 *chainx
= uchain
->ox
;
584 *chainy
= uchain
->oy
;
586 *cause_delay
= FALSE
;
588 if (dist2(x
, y
, uchain
->ox
, uchain
->oy
) <= 2) { /* nothing moved */
589 move_bc(1, *bc_control
, *ballx
, *bally
, *chainx
, *chainy
);
593 /* only need to move the chain? */
594 if (carried(uball
) || distmin(x
, y
, uball
->ox
, uball
->oy
) <= 2) {
595 coordxy oldchainx
= uchain
->ox
, oldchainy
= uchain
->oy
;
597 *bc_control
= BC_CHAIN
;
598 move_bc(1, *bc_control
, *ballx
, *bally
, *chainx
, *chainy
);
599 if (carried(uball
)) {
600 /* move chain only if necessary */
601 if (distmin(x
, y
, uchain
->ox
, uchain
->oy
) > 1) {
608 #define CHAIN_IN_MIDDLE(chx, chy) \
609 (distmin(x, y, chx, chy) <= 1 \
610 && distmin(chx, chy, uball->ox, uball->oy) <= 1)
611 #define IS_CHAIN_ROCK(x, y) \
612 (IS_OBSTRUCTED(levl[x][y].typ) \
613 || (IS_DOOR(levl[x][y].typ) \
614 && (levl[x][y].doormask & (D_CLOSED | D_LOCKED))))
616 * Don't ever move the chain into solid rock. If we have to, then
617 * instead undo the move_bc() and jump to the drag ball code. Note
618 * that this also means the "cannot carry and drag" message will not
619 * appear, since unless we moved at least two squares there is no
620 * possibility of the chain position being in solid rock.
622 #define SKIP_TO_DRAG \
624 *chainx = oldchainx; \
625 *chainy = oldchainy; \
626 move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \
630 if (IS_CHAIN_ROCK(u
.ux
, u
.uy
) || IS_CHAIN_ROCK(*chainx
, *chainy
)
631 || IS_CHAIN_ROCK(uball
->ox
, uball
->oy
))
632 already_in_rock
= TRUE
;
634 already_in_rock
= FALSE
;
636 switch (dist2(x
, y
, uball
->ox
, uball
->oy
)) {
637 /* two spaces diagonal from ball, move chain in-between */
639 *chainx
= (uball
->ox
+ x
) / 2;
640 *chainy
= (uball
->oy
+ y
) / 2;
641 if (IS_CHAIN_ROCK(*chainx
, *chainy
) && !already_in_rock
)
645 /* player is distance 2/1 from ball; move chain to one of the
652 coordxy tempx
, tempy
, tempx2
, tempy2
;
654 /* find position closest to current position of chain;
655 no effect if current position is already OK */
656 if (abs(x
- uball
->ox
) == 1) {
659 tempy
= tempy2
= (uball
->oy
+ y
) / 2;
661 tempx
= tempx2
= (uball
->ox
+ x
) / 2;
665 if (IS_CHAIN_ROCK(tempx
, tempy
) && !IS_CHAIN_ROCK(tempx2
, tempy2
)
666 && !already_in_rock
) {
668 /* Avoid pathological case *if* not teleporting:
670 * _X move northeast -----> X@
673 if (dist2(u
.ux
, u
.uy
, uball
->ox
, uball
->oy
) == 5
674 && dist2(x
, y
, tempx
, tempy
) == 1)
676 /* Avoid pathological case *if* not teleporting:
678 * _X move east -----> X_
681 if (dist2(u
.ux
, u
.uy
, uball
->ox
, uball
->oy
) == 4
682 && dist2(x
, y
, tempx
, tempy
) == 2)
687 } else if (!IS_CHAIN_ROCK(tempx
, tempy
)
688 && IS_CHAIN_ROCK(tempx2
, tempy2
) && !already_in_rock
) {
690 if (dist2(u
.ux
, u
.uy
, uball
->ox
, uball
->oy
) == 5
691 && dist2(x
, y
, tempx2
, tempy2
) == 1)
693 if (dist2(u
.ux
, u
.uy
, uball
->ox
, uball
->oy
) == 4
694 && dist2(x
, y
, tempx2
, tempy2
) == 2)
699 } else if (IS_CHAIN_ROCK(tempx
, tempy
)
700 && IS_CHAIN_ROCK(tempx2
, tempy2
) && !already_in_rock
) {
702 } else if (dist2(tempx
, tempy
, uchain
->ox
, uchain
->oy
)
703 < dist2(tempx2
, tempy2
, uchain
->ox
, uchain
->oy
)
704 || ((dist2(tempx
, tempy
, uchain
->ox
, uchain
->oy
)
705 == dist2(tempx2
, tempy2
, uchain
->ox
, uchain
->oy
))
716 /* ball is two spaces horizontal or vertical from player; move*/
717 /* chain in-between *unless* current chain position is OK */
719 if (CHAIN_IN_MIDDLE(uchain
->ox
, uchain
->oy
))
721 *chainx
= (x
+ uball
->ox
) / 2;
722 *chainy
= (y
+ uball
->oy
) / 2;
723 if (IS_CHAIN_ROCK(*chainx
, *chainy
) && !already_in_rock
)
727 /* ball is one space diagonal from player. Check for the
728 * following special case:
730 * _ moving southwest becomes @_
732 * (This will also catch teleporting that happens to resemble
733 * this case, but oh well.) Otherwise fall through.
736 if (dist2(x
, y
, uball
->ox
, uball
->oy
) == 2
737 && dist2(x
, y
, uchain
->ox
, uchain
->oy
) == 4) {
742 if (IS_CHAIN_ROCK(*chainx
, *chainy
) && !already_in_rock
)
750 /* do nothing if possible */
751 if (CHAIN_IN_MIDDLE(uchain
->ox
, uchain
->oy
))
753 /* otherwise try to drag chain to player's old position */
754 if (CHAIN_IN_MIDDLE(u
.ux
, u
.uy
)) {
759 /* otherwise use player's new position (they must have
760 teleported, for this to happen) */
766 impossible("bad chain movement");
770 #undef CHAIN_IN_MIDDLE
776 if (near_capacity() > SLT_ENCUMBER
&& dist2(x
, y
, u
.ux
, u
.uy
) <= 2) {
777 You("cannot %sdrag the heavy iron ball.",
778 gi
.invent
? "carry all that and also " : "");
783 if ((is_pool(uchain
->ox
, uchain
->oy
)
784 /* water not mere continuation of previous water */
785 && (levl
[uchain
->ox
][uchain
->oy
].typ
== POOL
786 || !is_pool(uball
->ox
, uball
->oy
)
787 || levl
[uball
->ox
][uball
->oy
].typ
== POOL
))
788 || ((t
= t_at(uchain
->ox
, uchain
->oy
))
789 && (is_pit(t
->ttyp
) || is_hole(t
->ttyp
)))) {
791 You_feel("a tug from the iron ball.");
795 struct monst
*victim
;
797 You("are jerked back by the iron ball!");
798 if ((victim
= m_at(uchain
->ox
, uchain
->oy
)) != 0) {
800 int dieroll
= rnd(20);
802 tmp
= -2 + Luck
+ find_mac(victim
);
803 tmp
+= omon_adj(victim
, uball
, TRUE
);
806 (void) hmon(victim
, uball
, HMON_DRAGGED
, dieroll
);
808 miss(xname(uball
), victim
);
810 } /* now check again in case mon died */
811 if (!m_at(uchain
->ox
, uchain
->oy
)) {
814 newsym(u
.ux0
, u
.uy0
);
818 *bc_control
= BC_BALL
;
819 move_bc(1, *bc_control
, *ballx
, *bally
, *chainx
, *chainy
);
822 move_bc(0, *bc_control
, *ballx
, *bally
, *chainx
, *chainy
);
828 *bc_control
= BC_BALL
| BC_CHAIN
;
830 move_bc(1, *bc_control
, *ballx
, *bally
, *chainx
, *chainy
);
831 if (dist2(x
, y
, u
.ux
, u
.uy
) > 2) {
832 /* Awful case: we're still in range of the ball, so we thought we
833 * could only move the chain, but it turned out that the target
834 * square for the chain was rock, so we had to drag it instead.
835 * But we can't drag it either, because we teleported and are more
836 * than one square from our old position. Revert to the teleport
839 *ballx
= *chainx
= x
;
840 *bally
= *chainy
= y
;
842 coordxy newchainx
= u
.ux
, newchainy
= u
.uy
;
845 * Generally, chain moves to hero's previous location and ball
846 * moves to chain's previous location, except that we try to
847 * keep the chain directly between the hero and the ball. But,
848 * take the simple approach if the hero's previous location or
849 * the potential between location is inaccessible.
851 if (dist2(x
, y
, uchain
->ox
, uchain
->oy
) == 4
852 && !IS_CHAIN_ROCK(newchainx
, newchainy
)) {
853 newchainx
= (x
+ uchain
->ox
) / 2;
854 newchainy
= (y
+ uchain
->oy
) / 2;
855 if (IS_CHAIN_ROCK(newchainx
, newchainy
)) {
856 /* don't let chain move to inaccessible location */
875 * The punished hero drops or throws her iron ball. If the hero is
876 * blind, we must reset the order and glyph. Check for side effects.
877 * This routine expects the ball to be already placed.
879 * Should not be called while swallowed.
882 drop_ball(coordxy x
, coordxy y
)
886 u
.bc_order
= bc_order();
888 u
.bglyph
= (u
.bc_order
) ? u
.cglyph
: levl
[x
][y
].glyph
;
891 if (x
!= u
.ux
|| y
!= u
.uy
) {
892 static const char pullmsg
[] = "The ball pulls you out of the ";
897 && u
.utraptype
!= TT_INFLOOR
&& u
.utraptype
!= TT_BURIEDBALL
) {
898 switch (u
.utraptype
) {
900 pline("%s%s!", pullmsg
, "pit");
903 pline("%s%s!", pullmsg
, "web");
904 Soundeffect(se_destroy_web
, 30);
905 pline_The("web is destroyed!");
906 deltrap(t_at(u
.ux
, u
.uy
));
909 pline("%s%s!", pullmsg
, hliquid("lava"));
912 side
= rn2(3) ? LEFT_SIDE
: RIGHT_SIDE
;
913 pline("%s%s!", pullmsg
, "bear trap");
914 set_wounded_legs(side
, rn1(1000, 500));
916 Your("%s %s is severely damaged.",
917 (side
== LEFT_SIDE
) ? "left" : "right",
919 losehp(Maybe_Half_Phys(2),
920 "leg damage from being pulled out of a bear trap",
926 fill_pit(u
.ux
, u
.uy
);
931 if (!Levitation
&& !MON_AT(x
, y
) && !u
.utrap
935 || is_hole(t
->ttyp
))))) {
942 gv
.vision_full_recalc
= 1; /* hero has moved, recalc vision later */
945 /* drop glyph under the chain */
946 if (u
.bc_felt
& BC_CHAIN
)
947 levl
[uchain
->ox
][uchain
->oy
].glyph
= u
.cglyph
;
948 u
.bc_felt
= 0; /* feel nothing */
949 /* pick up new glyph */
950 u
.cglyph
= (u
.bc_order
) ? u
.bglyph
: levl
[u
.ux
][u
.uy
].glyph
;
952 movobj(uchain
, u
.ux
, u
.uy
); /* has a newsym */
954 u
.bc_order
= bc_order();
956 newsym(u
.ux0
, u
.uy0
); /* clean up old position */
957 if (u
.ux0
!= u
.ux
|| u
.uy0
!= u
.uy
) {
963 /* ball&chain cause hero to randomly lose stuff from inventory */
967 struct obj
*otmp
, *nextobj
= 0;
968 int capacity
= weight_cap();
970 for (otmp
= gi
.invent
; otmp
; otmp
= nextobj
) {
971 nextobj
= otmp
->nobj
;
972 if (otmp
!= uball
&& rnd(capacity
) <= (int) otmp
->owt
) {
973 if (canletgo(otmp
, "")) {
974 You("drop %s and %s %s down the stairs with you.",
975 yname(otmp
), (otmp
->quan
== 1L) ? "it" : "they",
976 otense(otmp
, "fall"));
979 hitfloor(otmp
, FALSE
);
989 uchar dragchance
= 3;
992 * Assume that the ball falls forward if:
994 * a) the character is wielding it, or
995 * b) the character has both hands available to hold it (i.e. is
996 * not wielding any weapon), or
997 * c) (perhaps) it falls forward out of his non-weapon hand
999 forward
= carried(uball
) && (uwep
== uball
|| !uwep
|| !rn2(3));
1001 if (carried(uball
) && !welded(uball
))
1002 You("lose your grip on the iron ball.");
1004 cls(); /* previous level is still displayed although you
1005 went down the stairs. Avoids bug C343-20 */
1009 pline_The("iron ball drags you downstairs!");
1010 losehp(Maybe_Half_Phys(rnd(6)),
1011 "dragged downstairs by an iron ball", NO_KILLER_PREFIX
);
1016 Soundeffect(se_iron_ball_hits_you
, 25);
1017 pline_The("iron ball smacks into you!");
1018 losehp(Maybe_Half_Phys(rnd(20)), "iron ball collision",
1020 exercise(A_STR
, FALSE
);
1023 if ((int) dragchance
>= rnd(6)) {
1024 pline_The("iron ball drags you downstairs!");
1025 losehp(Maybe_Half_Phys(rnd(3)),
1026 "dragged downstairs by an iron ball", NO_KILLER_PREFIX
);
1027 exercise(A_STR
, FALSE
);
1034 bc_sanity_check(void)
1036 int otyp
, freeball
, freechain
;
1039 if (Punished
&& (!uball
|| !uchain
)) {
1040 impossible("Punished without %s%s%s?",
1041 !uball
? "iron ball" : "",
1042 (!uball
&& !uchain
) ? " and " : "",
1043 !uchain
? "attached chain" : "");
1044 } else if (!Punished
&& (uball
|| uchain
)) {
1045 impossible("Attached %s%s%s without being Punished?",
1046 uchain
? "chain" : "",
1047 (uchain
&& uball
) ? " and " : "",
1048 uball
? "iron ball" : "");
1050 /* ball is free when swallowed, when changing levels or during air bubble
1051 management on Plane of Water (both of which start and end in between
1052 sanity checking cycles, so shouldn't be relevant), other times? */
1053 freechain
= (!uchain
|| uchain
->where
== OBJ_FREE
);
1054 freeball
= (!uball
|| uball
->where
== OBJ_FREE
1055 /* lie to simplify the testing logic */
1056 || (freechain
&& uball
->where
== OBJ_INVENT
));
1057 if (uball
&& (uball
->otyp
!= HEAVY_IRON_BALL
1058 || (uball
->where
!= OBJ_FLOOR
1059 && uball
->where
!= OBJ_INVENT
1060 && uball
->where
!= OBJ_FREE
)
1061 || (freeball
^ freechain
)
1062 || (uball
->owornmask
& W_BALL
) == 0L
1063 || (uball
->owornmask
& ~(W_BALL
| W_WEAPONS
)) != 0L)) {
1065 onam
= safe_typename(otyp
);
1066 impossible("uball: type %d (%s), where %d, wornmask=0x%08lx",
1067 otyp
, onam
, uball
->where
, uball
->owornmask
);
1069 /* similar check to ball except can't be in inventory */
1070 if (uchain
&& (uchain
->otyp
!= IRON_CHAIN
1071 || (uchain
->where
!= OBJ_FLOOR
1072 && uchain
->where
!= OBJ_FREE
)
1073 || (freechain
^ freeball
)
1074 /* [could simplify this to owornmask != W_CHAIN] */
1075 || (uchain
->owornmask
& W_CHAIN
) == 0L
1076 || (uchain
->owornmask
& ~W_CHAIN
) != 0L)) {
1077 otyp
= uchain
->otyp
;
1078 onam
= safe_typename(otyp
);
1079 impossible("uchain: type %d (%s), where %d, wornmask=0x%08lx",
1080 otyp
, onam
, uchain
->where
, uchain
->owornmask
);
1082 if (uball
&& uchain
&& !(freeball
&& freechain
)) {
1083 int bx
, by
, cx
, cy
, bdx
, bdy
, cdx
, cdy
;
1085 /* non-free chain should be under or next to the hero;
1086 non-free ball should be on or next to the chain or else carried */
1087 cx
= uchain
->ox
, cy
= uchain
->oy
;
1088 cdx
= cx
- u
.ux
, cdy
= cy
- u
.uy
;
1089 cdx
= abs(cdx
), cdy
= abs(cdy
);
1090 if (uball
->where
== OBJ_INVENT
) /* carried(uball) */
1091 bx
= u
.ux
, by
= u
.uy
; /* get_obj_location() */
1093 bx
= uball
->ox
, by
= uball
->oy
;
1094 bdx
= bx
- cx
, bdy
= by
- cy
;
1095 bdx
= abs(bdx
), bdy
= abs(bdy
);
1096 if (cdx
> 1 || cdy
> 1 || bdx
> 1 || bdy
> 1)
1098 "b&c distance: you@<%d,%d>, chain@<%d,%d>, ball@<%d,%d>",
1099 u
.ux
, u
.uy
, cx
, cy
, bx
, by
);
1101 /* [check bc_order too?] */