1 /* NetHack 3.6 ball.c $NHDT-Date: 1573940835 2019/11/16 21:47:15 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.44 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) David Cohrs, 2006. */
4 /* NetHack may be freely redistributed. See license for details. */
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
9 /* JNetHack may be freely redistributed. See license for details. */
12 * =============================================================*/
16 STATIC_DCL int NDECL(bc_order);
17 STATIC_DCL void NDECL(litter);
18 STATIC_OVL void NDECL(placebc_core);
19 STATIC_OVL void NDECL(unplacebc_core);
20 STATIC_DCL boolean FDECL(check_restriction, (int));
22 static int bcrestriction = 0;
24 static struct breadcrumbs bcpbreadcrumbs = {0}, bcubreadcrumbs = {0};
34 pline("Startled, you drop the iron ball.");
36 pline("
\8bÁ
\82¢
\82Ä
\82 \82È
\82½
\82Í
\93S
\8b\85\82ð
\97\8e\82µ
\82½
\81D");
38 setuwep((struct obj *) 0);
39 if (uswapwep == uball)
40 setuswapwep((struct obj *) 0);
42 setuqwep((struct obj *) 0);
43 /* [this used to test 'if (uwep != uball)' but that always passes
44 after the setuwep() above] */
45 freeinv(uball); /* remove from inventory but don't place on floor */
50 /* ball&chain might hit hero when falling through a trap door */
56 gets_hit = (((uball->ox != u.ux) || (uball->oy != u.uy))
57 && ((uwep == uball) ? FALSE : (boolean) rn2(5)));
63 pline_The("iron ball falls on your %s.", body_part(HEAD));
65 pline("
\93S
\8b\85\82Í
\82 \82È
\82½
\82Ì%s
\82Ì
\8fã
\82É
\97\8e\82¿
\82½
\81D", body_part(HEAD));
67 if (is_metallic(uarmh)) {
69 pline("Fortunately, you are wearing a hard helmet.");
71 pline("
\8dK
\89^
\82É
\82à
\81C
\82 \82È
\82½
\82Í
\8cÅ
\82¢
\8a\95\82ð
\90g
\82É
\82Â
\82¯
\82Ä
\82¢
\82½
\81D");
73 } else if (flags.verbose)
75 pline("%s does not protect you.", Yname2(uarmh));
77 Your("%s
\82Å
\82Í
\8eç
\82ê
\82È
\82¢
\81D", xname(uarmh));
80 losehp(Maybe_Half_Phys(dmg), "crunched in the head by an iron ball",
83 losehp(Maybe_Half_Phys(dmg), "
\93S
\8b\85\82Å
\93ª
\82ð
\91Å
\82Á
\82Ä", KILLED_BY);
89 * To make this work, we have to mess with the hero's mind. The rules for
92 * 1. If the hero can see them, fine.
93 * 2. If the hero can't see either, it isn't seen.
94 * 3. If either is felt it is seen.
95 * 4. If either is felt and moved, it disappears.
97 * If the hero can see, then when a move is done, the ball and chain are
98 * first picked up, the positions under them are corrected, then they
99 * are moved after the hero moves. Not too bad.
101 * If the hero is blind, then she can "feel" the ball and/or chain at any
102 * time. However, when the hero moves, the felt ball and/or chain become
103 * unfelt and whatever was felt "under" the ball&chain appears. Pretty
104 * nifty, but it requires that the ball&chain "remember" what was under
105 * them --- i.e. they pick-up glyphs when they are felt and drop them when
106 * moved (and felt). When swallowed, the ball&chain are pulled completely
107 * off of the dungeon, but are still on the object chain. They are placed
108 * under the hero when she is expelled.
113 * int u.bglyph glyph under the ball
114 * int u.cglyph glyph under the chain
115 * int u.bc_felt mask for ball/chain being felt
116 * #define BC_BALL 0x01 bit mask in u.bc_felt for ball
117 * #define BC_CHAIN 0x02 bit mask in u.bc_felt for chain
118 * int u.bc_order ball & chain order
120 * u.bc_felt is also manipulated in display.c and read.c, the others only
121 * in this file. None of these variables are valid unless the player is
125 /* values for u.bc_order */
126 #define BCPOS_DIFFER 0 /* ball & chain at different positions */
127 #define BCPOS_CHAIN 1 /* chain on top of ball */
128 #define BCPOS_BALL 2 /* ball on top of chain */
131 * Place the ball & chain under the hero. Make sure that the ball & chain
132 * variables are set (actually only needed when blind, but what the heck).
133 * It is assumed that when this is called, the ball and chain are NOT
134 * attached to the object list.
136 * Should not be called while swallowed except on waterlevel.
141 if (!uchain || !uball) {
142 impossible("Where are your ball and chain?");
146 (void) flooreffects(uchain, u.ux, u.uy, ""); /* chain might rust */
148 if (carried(uball)) { /* the ball is carried */
149 u.bc_order = BCPOS_DIFFER;
151 /* ball might rust -- already checked when carried */
152 (void) flooreffects(uball, u.ux, u.uy, "");
153 place_object(uball, u.ux, u.uy);
154 u.bc_order = BCPOS_CHAIN;
157 place_object(uchain, u.ux, u.uy);
159 u.bglyph = u.cglyph = levl[u.ux][u.uy].glyph; /* pick up glyph */
169 if (Is_waterlevel(&u.uz)) {
170 /* we need to proceed with the removal from the floor
171 * so that movebubbles() processing will disregard it as
172 * intended. Ignore all the vision stuff.
175 obj_extract_self(uball);
176 obj_extract_self(uchain);
178 /* ball&chain not unplaced while swallowed */
182 if (!carried(uball)) {
183 obj_extract_self(uball);
184 if (Blind && (u.bc_felt & BC_BALL)) /* drop glyph */
185 levl[uball->ox][uball->oy].glyph = u.bglyph;
187 newsym(uball->ox, uball->oy);
189 obj_extract_self(uchain);
190 if (Blind && (u.bc_felt & BC_CHAIN)) /* drop glyph */
191 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
193 newsym(uchain->ox, uchain->oy);
194 u.bc_felt = 0; /* feel nothing */
198 check_restriction(restriction)
203 if (!bcrestriction || (restriction == override_restriction))
206 ret = (bcrestriction == restriction) ? TRUE : FALSE;
214 if (!check_restriction(0)) {
215 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
216 char panicbuf[BUFSZ];
218 Sprintf(panicbuf, "placebc denied, restriction in effect");
219 paniclog("placebc", panicbuf);
223 if (uchain && uchain->where != OBJ_FREE) {
224 impossible("bc already placed?");
234 impossible("unplacebc denied, restriction in place");
241 unplacebc_and_covet_placebc()
246 impossible("unplacebc_and_covet_placebc denied, already restricted");
248 restriction = bcrestriction = rnd(400);
255 lift_covet_and_placebc(pin)
258 if (!check_restriction(pin)) {
259 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
260 char panicbuf[BUFSZ];
262 Sprintf(panicbuf, "lift_covet_and_placebc denied, %s",
263 (pin != bcrestriction) ? "pin mismatch"
264 : "restriction in effect");
265 paniclog("placebc", panicbuf);
269 if (uchain && uchain->where != OBJ_FREE) {
270 impossible("bc already placed?");
276 #else /* BREADCRUMBS */
279 Placebc(funcnm, linenum)
283 if (!check_restriction(0)) {
284 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
285 char panicbuf[BUFSZ];
287 Sprintf(panicbuf, "Placebc denied to %s:%d, restricted by %s:%d",
289 bcpbreadcrumbs.funcnm, bcpbreadcrumbs.linenum);
290 paniclog("Placebc", panicbuf);
294 if ((uchain && uchain->where != OBJ_FREE)
295 && bcpbreadcrumbs.in_effect) {
296 impossible("Placebc collision at %s:%d, already placed by %s:%d",
298 bcpbreadcrumbs.funcnm, bcpbreadcrumbs.linenum);
301 bcpbreadcrumbs.in_effect = TRUE;
302 bcubreadcrumbs.in_effect = FALSE;
303 bcpbreadcrumbs.funcnm = funcnm;
304 bcpbreadcrumbs.linenum = linenum;
309 Unplacebc(funcnm, linenum)
315 char panicbuf[BUFSZ];
317 Sprintf(panicbuf, "Unplacebc from %s:%d, when restricted to %s:%d",
319 bcubreadcrumbs.funcnm, bcubreadcrumbs.linenum);
320 paniclog("Unplacebc", panicbuf);
322 bcpbreadcrumbs.in_effect = FALSE;
323 bcubreadcrumbs.in_effect = TRUE;
324 bcubreadcrumbs.funcnm = funcnm;
325 bcubreadcrumbs.linenum = linenum;
330 Unplacebc_and_covet_placebc(funcnm, linenum)
338 "Unplacebc_and_covet_placebc denied to %s:%d, restricted by %s:%d",
340 bcubreadcrumbs.funcnm, bcubreadcrumbs.linenum);
342 restriction = bcrestriction = rnd(400);
343 bcpbreadcrumbs.in_effect = FALSE;
344 bcubreadcrumbs.in_effect = TRUE;
345 bcubreadcrumbs.funcnm = funcnm;
346 bcubreadcrumbs.linenum = linenum;
353 Lift_covet_and_placebc(pin, funcnm, linenum)
358 if (!check_restriction(pin)) {
359 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
360 char panicbuf[BUFSZ];
363 "Lift_covet_and_placebc denied to %s:%d, restricted by %s:%d",
365 bcpbreadcrumbs.funcnm, bcpbreadcrumbs.linenum);
366 paniclog("Lift_covet_and_placebc", panicbuf);
370 if (uchain && uchain->where != OBJ_FREE) {
371 impossible("bc already placed?");
376 #endif /* BREADCRUMBS */
379 * Return the stacking of the hero's ball & chain. This assumes that the
380 * hero is being punished.
387 if (uchain->ox != uball->ox || uchain->oy != uball->oy || carried(uball)
391 for (obj = level.objects[uball->ox][uball->oy]; obj;
392 obj = obj->nexthere) {
398 impossible("bc_order: ball&chain not in same location!");
405 * The hero is either about to go blind or already blind and just punished.
406 * Set up the ball and chain variables so that the ball and chain are "felt".
409 set_bc(already_blind)
412 int ball_on_floor = !carried(uball);
414 u.bc_order = bc_order(); /* get the order */
415 u.bc_felt = ball_on_floor ? BC_BALL | BC_CHAIN : BC_CHAIN; /* felt */
417 if (already_blind || u.uswallow) {
418 u.cglyph = u.bglyph = levl[u.ux][u.uy].glyph;
423 * Since we can still see, remove the ball&chain and get the glyph that
424 * would be beneath them. Then put the ball&chain back. This is pretty
425 * disgusting, but it will work.
427 remove_object(uchain);
429 remove_object(uball);
431 newsym(uchain->ox, uchain->oy);
432 u.cglyph = levl[uchain->ox][uchain->oy].glyph;
434 if (u.bc_order == BCPOS_DIFFER) { /* different locations */
435 place_object(uchain, uchain->ox, uchain->oy);
436 newsym(uchain->ox, uchain->oy);
438 newsym(uball->ox, uball->oy); /* see under ball */
439 u.bglyph = levl[uball->ox][uball->oy].glyph;
440 place_object(uball, uball->ox, uball->oy);
441 newsym(uball->ox, uball->oy); /* restore ball */
445 if (u.bc_order == BCPOS_CHAIN) {
446 place_object(uball, uball->ox, uball->oy);
447 place_object(uchain, uchain->ox, uchain->oy);
449 place_object(uchain, uchain->ox, uchain->oy);
450 place_object(uball, uball->ox, uball->oy);
452 newsym(uball->ox, uball->oy);
459 * Move the ball and chain. This is called twice for every move. The first
460 * time to pick up the ball and chain before the move, the second time to
461 * place the ball and chain after the move. If the ball is carried, this
462 * function should never have BC_BALL as part of its control.
464 * Should not be called while swallowed.
467 move_bc(before, control, ballx, bally, chainx, chainy)
469 xchar ballx, bally, chainx, chainy; /* only matter !before */
473 * The hero is blind. Time to work hard. The ball and chain that
474 * are attached to the hero are very special. The hero knows that
475 * they are attached, so when they move, the hero knows that they
476 * aren't at the last position remembered. This is complicated
477 * by the fact that the hero can "feel" the surrounding locations
478 * at any time, hence, making one or both of them show up again.
479 * So, we have to keep track of which is felt at any one time and
483 if ((control & BC_CHAIN) && (control & BC_BALL)) {
485 * Both ball and chain moved. If felt, drop glyph.
487 if (u.bc_felt & BC_BALL)
488 levl[uball->ox][uball->oy].glyph = u.bglyph;
489 if (u.bc_felt & BC_CHAIN)
490 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
493 /* Pick up glyph at new location. */
494 u.bglyph = levl[ballx][bally].glyph;
495 u.cglyph = levl[chainx][chainy].glyph;
497 movobj(uball, ballx, bally);
498 movobj(uchain, chainx, chainy);
499 } else if (control & BC_BALL) {
500 if (u.bc_felt & BC_BALL) {
501 if (u.bc_order == BCPOS_DIFFER) { /* ball by itself */
502 levl[uball->ox][uball->oy].glyph = u.bglyph;
503 } else if (u.bc_order == BCPOS_BALL) {
504 if (u.bc_felt & BC_CHAIN) { /* know chain is there */
505 map_object(uchain, 0);
507 levl[uball->ox][uball->oy].glyph = u.bglyph;
510 u.bc_felt &= ~BC_BALL; /* no longer feel the ball */
513 /* Pick up glyph at new position. */
514 u.bglyph = (ballx != chainx || bally != chainy)
515 ? levl[ballx][bally].glyph
518 movobj(uball, ballx, bally);
519 } else if (control & BC_CHAIN) {
520 if (u.bc_felt & BC_CHAIN) {
521 if (u.bc_order == BCPOS_DIFFER) {
522 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
523 } else if (u.bc_order == BCPOS_CHAIN) {
524 if (u.bc_felt & BC_BALL) {
525 map_object(uball, 0);
527 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
530 u.bc_felt &= ~BC_CHAIN;
532 /* Pick up glyph at new position. */
533 u.cglyph = (ballx != chainx || bally != chainy)
534 ? levl[chainx][chainy].glyph
537 movobj(uchain, chainx, chainy);
540 u.bc_order = bc_order(); /* reset the order */
545 * The hero is not blind. To make this work correctly, we need to
546 * pick up the ball and chain before the hero moves, then put them
547 * in their new positions after the hero moves.
552 * Neither ball nor chain is moving, so remember which was
553 * on top until !before. Use the variable u.bc_order
554 * since it is only valid when blind.
556 u.bc_order = bc_order();
559 remove_object(uchain);
560 newsym(uchain->ox, uchain->oy);
561 if (!carried(uball)) {
562 remove_object(uball);
563 newsym(uball->ox, uball->oy);
566 int on_floor = !carried(uball);
568 if ((control & BC_CHAIN)
569 || (!control && u.bc_order == BCPOS_CHAIN)) {
570 /* If the chain moved or nothing moved & chain on top. */
572 place_object(uball, ballx, bally);
573 place_object(uchain, chainx, chainy); /* chain on top */
575 place_object(uchain, chainx, chainy);
577 place_object(uball, ballx, bally);
580 newsym(chainx, chainy);
582 newsym(ballx, bally);
587 /* return TRUE if the caller needs to place the ball and chain down again */
589 drag_ball(x, y, bc_control, ballx, bally, chainx, chainy, cause_delay,
593 xchar *ballx, *bally, *chainx, *chainy;
594 boolean *cause_delay;
597 struct trap *t = (struct trap *) 0;
598 boolean already_in_rock;
601 * Should not be called while swallowed. Should be called before
602 * movement, because we might want to move the ball or chain to the
603 * hero's old position.
605 * It is called if we are moving. It is also called if we are
606 * teleporting *if* the ball doesn't move and we thus must drag the
607 * chain. It is not called for ordinary teleportation.
609 * 'allow_drag' is only used in the ugly special case where teleporting
610 * must drag the chain, while an identical-looking movement must drag
611 * both the ball and chain.
616 *chainx = uchain->ox;
617 *chainy = uchain->oy;
619 *cause_delay = FALSE;
621 if (dist2(x, y, uchain->ox, uchain->oy) <= 2) { /* nothing moved */
622 move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
626 /* only need to move the chain? */
627 if (carried(uball) || distmin(x, y, uball->ox, uball->oy) <= 2) {
628 xchar oldchainx = uchain->ox, oldchainy = uchain->oy;
630 *bc_control = BC_CHAIN;
631 move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
632 if (carried(uball)) {
633 /* move chain only if necessary */
634 if (distmin(x, y, uchain->ox, uchain->oy) > 1) {
641 #define CHAIN_IN_MIDDLE(chx, chy) \
642 (distmin(x, y, chx, chy) <= 1 \
643 && distmin(chx, chy, uball->ox, uball->oy) <= 1)
644 #define IS_CHAIN_ROCK(x, y) \
645 (IS_ROCK(levl[x][y].typ) \
646 || (IS_DOOR(levl[x][y].typ) \
647 && (levl[x][y].doormask & (D_CLOSED | D_LOCKED))))
649 * Don't ever move the chain into solid rock. If we have to, then
650 * instead undo the move_bc() and jump to the drag ball code. Note
651 * that this also means the "cannot carry and drag" message will not
652 * appear, since unless we moved at least two squares there is no
653 * possibility of the chain position being in solid rock.
655 #define SKIP_TO_DRAG \
657 *chainx = oldchainx; \
658 *chainy = oldchainy; \
659 move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \
663 if (IS_CHAIN_ROCK(u.ux, u.uy) || IS_CHAIN_ROCK(*chainx, *chainy)
664 || IS_CHAIN_ROCK(uball->ox, uball->oy))
665 already_in_rock = TRUE;
667 already_in_rock = FALSE;
669 switch (dist2(x, y, uball->ox, uball->oy)) {
670 /* two spaces diagonal from ball, move chain inbetween */
672 *chainx = (uball->ox + x) / 2;
673 *chainy = (uball->oy + y) / 2;
674 if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
678 /* player is distance 2/1 from ball; move chain to one of the
685 xchar tempx, tempy, tempx2, tempy2;
687 /* find position closest to current position of chain;
688 no effect if current position is already OK */
689 if (abs(x - uball->ox) == 1) {
692 tempy = tempy2 = (uball->oy + y) / 2;
694 tempx = tempx2 = (uball->ox + x) / 2;
698 if (IS_CHAIN_ROCK(tempx, tempy) && !IS_CHAIN_ROCK(tempx2, tempy2)
699 && !already_in_rock) {
701 /* Avoid pathological case *if* not teleporting:
703 * _X move northeast -----> X@
706 if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5
707 && dist2(x, y, tempx, tempy) == 1)
709 /* Avoid pathological case *if* not teleporting:
711 * _X move east -----> X_
714 if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4
715 && dist2(x, y, tempx, tempy) == 2)
720 } else if (!IS_CHAIN_ROCK(tempx, tempy)
721 && IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
723 if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5
724 && dist2(x, y, tempx2, tempy2) == 1)
726 if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4
727 && dist2(x, y, tempx2, tempy2) == 2)
732 } else if (IS_CHAIN_ROCK(tempx, tempy)
733 && IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
735 } else if (dist2(tempx, tempy, uchain->ox, uchain->oy)
736 < dist2(tempx2, tempy2, uchain->ox, uchain->oy)
737 || ((dist2(tempx, tempy, uchain->ox, uchain->oy)
738 == dist2(tempx2, tempy2, uchain->ox, uchain->oy))
749 /* ball is two spaces horizontal or vertical from player; move*/
750 /* chain inbetween *unless* current chain position is OK */
752 if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
754 *chainx = (x + uball->ox) / 2;
755 *chainy = (y + uball->oy) / 2;
756 if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
760 /* ball is one space diagonal from player. Check for the
761 * following special case:
763 * _ moving southwest becomes @_
765 * (This will also catch teleporting that happens to resemble
766 * this case, but oh well.) Otherwise fall through.
769 if (dist2(x, y, uball->ox, uball->oy) == 2
770 && dist2(x, y, uchain->ox, uchain->oy) == 4) {
775 if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
782 /* do nothing if possible */
783 if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
785 /* otherwise try to drag chain to player's old position */
786 if (CHAIN_IN_MIDDLE(u.ux, u.uy)) {
791 /* otherwise use player's new position (they must have
792 teleported, for this to happen) */
798 impossible("bad chain movement");
802 #undef CHAIN_IN_MIDDLE
808 if (near_capacity() > SLT_ENCUMBER && dist2(x, y, u.ux, u.uy) <= 2) {
810 You("cannot %sdrag the heavy iron ball.",
811 invent ? "carry all that and also " : "");
813 You("%s
\8fd
\82¢
\93S
\8b\85\82ð
\82Ð
\82«
\82¸
\82é
\82±
\82Æ
\82ª
\82Å
\82«
\82È
\82¢
\81D",
814 invent ? "
\82»
\82ê
\82¾
\82¯
\82Ì
\89×
\95¨
\82ð
\8e\9d\82Á
\82½
\82Ü
\82Ü" : "");
820 if ((is_pool(uchain->ox, uchain->oy)
821 /* water not mere continuation of previous water */
822 && (levl[uchain->ox][uchain->oy].typ == POOL
823 || !is_pool(uball->ox, uball->oy)
824 || levl[uball->ox][uball->oy].typ == POOL))
825 || ((t = t_at(uchain->ox, uchain->oy))
826 && (is_pit(t->ttyp) || is_hole(t->ttyp)))) {
829 You_feel("a tug from the iron ball.");
831 You("
\93S
\8b\85\82É
\88ø
\82Á
\82Ï
\82ç
\82ê
\82½
\81D");
835 struct monst *victim;
838 You("are jerked back by the iron ball!");
840 You("
\93S
\8b\85\82É
\82®
\82¢
\82Æ
\88ø
\82Á
\82Ï
\82ç
\82ê
\82½
\81I");
841 if ((victim = m_at(uchain->ox, uchain->oy)) != 0) {
843 int dieroll = rnd(20);
845 tmp = -2 + Luck + find_mac(victim);
846 tmp += omon_adj(victim, uball, TRUE);
849 (void) hmon(victim, uball, HMON_DRAGGED, dieroll);
851 miss(xname(uball), victim);
853 } /* now check again in case mon died */
854 if (!m_at(uchain->ox, uchain->oy)) {
857 newsym(u.ux0, u.uy0);
861 *bc_control = BC_BALL;
862 move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
865 move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy);
871 *bc_control = BC_BALL | BC_CHAIN;
873 move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
874 if (dist2(x, y, u.ux, u.uy) > 2) {
875 /* Awful case: we're still in range of the ball, so we thought we
876 * could only move the chain, but it turned out that the target
877 * square for the chain was rock, so we had to drag it instead.
878 * But we can't drag it either, because we teleported and are more
879 * than one square from our old position. Revert to the teleport
882 *ballx = *chainx = x;
883 *bally = *chainy = y;
885 xchar newchainx = u.ux, newchainy = u.uy;
888 * Generally, chain moves to hero's previous location and ball
889 * moves to chain's previous location, except that we try to
890 * keep the chain directly between the hero and the ball. But,
891 * take the simple approach if the hero's previous location or
892 * the potential between location is inaccessible.
894 if (dist2(x, y, uchain->ox, uchain->oy) == 4
895 && !IS_CHAIN_ROCK(newchainx, newchainy)) {
896 newchainx = (x + uchain->ox) / 2;
897 newchainy = (y + uchain->oy) / 2;
898 if (IS_CHAIN_ROCK(newchainx, newchainy)) {
899 /* don't let chain move to inaccessible location */
918 * The punished hero drops or throws her iron ball. If the hero is
919 * blind, we must reset the order and glyph. Check for side effects.
920 * This routine expects the ball to be already placed.
922 * Should not be called while swallowed.
930 u.bc_order = bc_order();
932 u.bglyph = (u.bc_order) ? u.cglyph : levl[x][y].glyph;
935 if (x != u.ux || y != u.uy) {
937 static const char *pullmsg = "The ball pulls you out of the %s!";
939 static const char *pullmsg = "
\93S
\8b\85\82Í%s
\82©
\82ç
\82 \82È
\82½
\82ð
\88ø
\82Á
\82Ï
\82è
\8fo
\82µ
\82½
\81I";
944 && u.utraptype != TT_INFLOOR && u.utraptype != TT_BURIEDBALL) {
945 switch (u.utraptype) {
948 pline(pullmsg, "pit");
950 pline(pullmsg, "
\97\8e\82µ
\8c\8a");
954 pline(pullmsg, "web");
956 pline(pullmsg, "
\82
\82à
\82Ì
\91\83");
958 pline_The("web is destroyed!");
960 pline("
\82
\82à
\82Ì
\91\83\82Í
\82±
\82í
\82ê
\82½
\81I");
961 deltrap(t_at(u.ux, u.uy));
965 pline(pullmsg, hliquid("lava"));
967 pline(pullmsg, hliquid("
\97n
\8aâ"));
970 side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
972 pline(pullmsg, "bear trap");
974 pline(pullmsg, "
\8cF
\82Ìã©");
975 set_wounded_legs(side, rn1(1000, 500));
978 Your("%s %s is severely damaged.",
979 (side == LEFT_SIDE) ? "left" : "right",
982 Your("%s%s
\82Í
\82Ð
\82Ç
\82¢
\8f\9d\82ð
\95\89\82Á
\82½
\81D",
983 (side == LEFT_SIDE) ? "
\8d¶" : "
\89E",
987 losehp(Maybe_Half_Phys(2),
988 "leg damage from being pulled out of a bear trap",
991 losehp(Maybe_Half_Phys(2),
992 "
\8cF
\82Ìã©
\82©
\82ç
\94²
\82¯
\82æ
\82¤
\82Æ
\91«
\82ð
\88ø
\82Á
\82Ï
\82Á
\82Ä",
999 fill_pit(u.ux, u.uy);
1004 if (!Levitation && !MON_AT(x, y) && !u.utrap
1006 || ((t = t_at(x, y))
1008 || is_hole(t->ttyp))))) {
1015 vision_full_recalc = 1; /* hero has moved, recalculate vision later */
1018 /* drop glyph under the chain */
1019 if (u.bc_felt & BC_CHAIN)
1020 levl[uchain->ox][uchain->oy].glyph = u.cglyph;
1021 u.bc_felt = 0; /* feel nothing */
1022 /* pick up new glyph */
1023 u.cglyph = (u.bc_order) ? u.bglyph : levl[u.ux][u.uy].glyph;
1025 movobj(uchain, u.ux, u.uy); /* has a newsym */
1027 u.bc_order = bc_order();
1029 newsym(u.ux0, u.uy0); /* clean up old position */
1030 if (u.ux0 != u.ux || u.uy0 != u.uy) {
1037 /* ball&chain cause hero to randomly lose stuff from inventory */
1041 struct obj *otmp, *nextobj = 0;
1042 int capacity = weight_cap();
1044 for (otmp = invent; otmp; otmp = nextobj) {
1045 nextobj = otmp->nobj;
1046 if ((otmp != uball) && (rnd(capacity) <= (int) otmp->owt)) {
1047 if (canletgo(otmp, "")) {
1049 You("drop %s and %s %s down the stairs with you.",
1050 yname(otmp), (otmp->quan == 1L) ? "it" : "they",
1051 otense(otmp, "fall"));
1053 You("%s
\82ð
\97\8e\82Æ
\82µ
\81C
\82»
\82ê
\82Í
\82 \82È
\82½
\82Æ
\88ê
\8f\8f\82É
\8aK
\92i
\82ð
\97\8e\82¿
\82Ä
\82¢
\82Á
\82½
\81D",
1057 encumber_msg(); /* drop[xyz]() probably ought to to this... */
1067 uchar dragchance = 3;
1070 * Assume that the ball falls forward if:
1072 * a) the character is wielding it, or
1073 * b) the character has both hands available to hold it (i.e. is
1074 * not wielding any weapon), or
1075 * c) (perhaps) it falls forward out of his non-weapon hand
1077 forward = carried(uball) && (uwep == uball || !uwep || !rn2(3));
1081 You("lose your grip on the iron ball.");
1083 You("
\93S
\8b\85\82ð
\8eè
\82©
\82ç
\97\8e\82µ
\82Ä
\82µ
\82Ü
\82Á
\82½
\81D");
1085 cls(); /* previous level is still displayed although you
1086 went down the stairs. Avoids bug C343-20 */
1091 pline_The("iron ball drags you downstairs!");
1093 You("
\93S
\8b\85\82É
\82æ
\82Á
\82Ä
\8aK
\92i
\82ð
\82±
\82ë
\82ª
\82è
\97\8e\82¿
\82½
\81I");
1094 losehp(Maybe_Half_Phys(rnd(6)),
1096 "dragged downstairs by an iron ball", NO_KILLER_PREFIX);
1098 "
\93S
\8b\85\82É
\82æ
\82è
\8aK
\92i
\82ð
\82±
\82ë
\82ª
\82è
\97\8e\82¿
\82Ä", KILLED_BY);
1104 pline_The("iron ball smacks into you!");
1106 pline("
\93S
\8b\85\82Í
\82 \82È
\82½
\82É
\83S
\83c
\83\93\82Æ
\82Ô
\82Â
\82©
\82Á
\82½
\81I");
1108 losehp(Maybe_Half_Phys(rnd(20)), "iron ball collision",
1110 losehp(Maybe_Half_Phys(rnd(20)), "
\93S
\8b\85\82Ì
\8fÕ
\93Ë
\82Å",
1112 exercise(A_STR, FALSE);
1115 if ((int) dragchance >= rnd(6)) {
1117 pline_The("iron ball drags you downstairs!");
1119 You("
\93S
\8b\85\82É
\82æ
\82Á
\82Ä
\8aK
\92i
\82ð
\82±
\82ë
\82ª
\82è
\97\8e\82¿
\82½
\81I");
1120 losehp(Maybe_Half_Phys(rnd(3)),
1122 "dragged downstairs by an iron ball", NO_KILLER_PREFIX);
1124 "
\93S
\8b\85\82É
\82æ
\82è
\8aK
\92i
\82ð
\82±
\82ë
\82ª
\82è
\97\8e\82¿
\82Ä", KILLED_BY);
1125 exercise(A_STR, FALSE);
1134 int otyp, freeball, freechain;
1137 if (Punished && (!uball || !uchain)) {
1138 impossible("Punished without %s%s%s?",
1139 !uball ? "iron ball" : "",
1140 (!uball && !uchain) ? " and " : "",
1141 !uchain ? "attached chain" : "");
1142 } else if (!Punished && (uball || uchain)) {
1143 impossible("Attached %s%s%s without being Punished?",
1144 uchain ? "chain" : "",
1145 (uchain && uball) ? " and " : "",
1146 uball ? "iron ball" : "");
1148 /* ball is free when swallowed, when changing levels or during air bubble
1149 management on Plane of Water (both of which start and end in between
1150 sanity checking cycles, so shouldn't be relevant), other times? */
1151 freechain = (!uchain || uchain->where == OBJ_FREE);
1152 freeball = (!uball || uball->where == OBJ_FREE
1153 /* lie to simplify the testing logic */
1154 || (freechain && uball->where == OBJ_INVENT));
1155 if (uball && (uball->otyp != HEAVY_IRON_BALL
1156 || (uball->where != OBJ_FLOOR
1157 && uball->where != OBJ_INVENT
1158 && uball->where != OBJ_FREE)
1159 || (freeball ^ freechain)
1160 || (uball->owornmask & W_BALL) == 0L
1161 || (uball->owornmask & ~(W_BALL | W_WEAPONS)) != 0L)) {
1163 onam = safe_typename(otyp);
1164 impossible("uball: type %d (%s), where %d, wornmask=0x%08lx",
1165 otyp, onam, uball->where, uball->owornmask);
1167 /* similar check to ball except can't be in inventory */
1168 if (uchain && (uchain->otyp != IRON_CHAIN
1169 || (uchain->where != OBJ_FLOOR
1170 && uchain->where != OBJ_FREE)
1171 || (freechain ^ freeball)
1172 /* [could simplify this to owornmask != W_CHAIN] */
1173 || (uchain->owornmask & W_CHAIN) == 0L
1174 || (uchain->owornmask & ~W_CHAIN) != 0L)) {
1175 otyp = uchain->otyp;
1176 onam = safe_typename(otyp);
1177 impossible("uchain: type %d (%s), where %d, wornmask=0x%08lx",
1178 otyp, onam, uchain->where, uchain->owornmask);
1180 if (uball && uchain && !(freeball && freechain)) {
1181 int bx, by, cx, cy, bdx, bdy, cdx, cdy;
1183 /* non-free chain should be under or next to the hero;
1184 non-free ball should be on or next to the chain or else carried */
1185 cx = uchain->ox, cy = uchain->oy;
1186 cdx = cx - u.ux, cdy = cy - u.uy;
1187 cdx = abs(cdx), cdy = abs(cdy);
1188 if (uball->where == OBJ_INVENT) /* carried(uball) */
1189 bx = u.ux, by = u.uy; /* get_obj_location() */
1191 bx = uball->ox, by = uball->oy;
1192 bdx = bx - cx, bdy = by - cy;
1193 bdx = abs(bdx), bdy = abs(bdy);
1194 if (cdx > 1 || cdy > 1 || bdx > 1 || bdy > 1)
1196 "b&c distance: you@<%d,%d>, chain@<%d,%d>, ball@<%d,%d>",
1197 u.ux, u.uy, cx, cy, bx, by);
1199 /* [check bc_order too?] */