OSDN Git Service

update year to 2020
[jnethack/source.git] / src / ball.c
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. */
5
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. */
10
11 /* Ball & Chain
12  * =============================================================*/
13
14 #include "hack.h"
15
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));
21
22 static int bcrestriction = 0;
23 #ifdef BREADCRUMBS
24 static struct breadcrumbs bcpbreadcrumbs = {0}, bcubreadcrumbs = {0};
25 #endif
26
27 void
28 ballrelease(showmsg)
29 boolean showmsg;
30 {
31     if (carried(uball)) {
32         if (showmsg)
33 /*JP
34             pline("Startled, you drop the iron ball.");
35 */
36             pline("\8bÁ\82¢\82Ä\82 \82È\82½\82Í\93S\8b\85\82ð\97\8e\82µ\82½\81D");
37         if (uwep == uball)
38             setuwep((struct obj *) 0);
39         if (uswapwep == uball)
40             setuswapwep((struct obj *) 0);
41         if (uquiver == uball)
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 */
46         encumber_msg();
47     }
48 }
49
50 /* ball&chain might hit hero when falling through a trap door */
51 void
52 ballfall()
53 {
54     boolean gets_hit;
55
56     gets_hit = (((uball->ox != u.ux) || (uball->oy != u.uy))
57                 && ((uwep == uball) ? FALSE : (boolean) rn2(5)));
58     ballrelease(TRUE);
59     if (gets_hit) {
60         int dmg = rn1(7, 25);
61
62 /*JP
63         pline_The("iron ball falls on your %s.", body_part(HEAD));
64 */
65         pline("\93S\8b\85\82Í\82 \82È\82½\82Ì%s\82Ì\8fã\82É\97\8e\82¿\82½\81D", body_part(HEAD));
66         if (uarmh) {
67             if (is_metallic(uarmh)) {
68 /*JP
69                 pline("Fortunately, you are wearing a hard helmet.");
70 */
71                 pline("\8dK\89^\82É\82à\81C\82 \82È\82½\82Í\8cÅ\82¢\8a\95\82ð\90g\82É\82Â\82¯\82Ä\82¢\82½\81D");
72                 dmg = 3;
73             } else if (flags.verbose)
74 /*JP
75                 pline("%s does not protect you.", Yname2(uarmh));
76 */
77                 Your("%s\82Å\82Í\8eç\82ê\82È\82¢\81D", xname(uarmh));
78         }
79 #if 0 /*JP*/
80         losehp(Maybe_Half_Phys(dmg), "crunched in the head by an iron ball",
81                NO_KILLER_PREFIX);
82 #else
83         losehp(Maybe_Half_Phys(dmg), "\93S\8b\85\82Å\93ª\82ð\91Å\82Á\82Ä", KILLED_BY);
84 #endif
85     }
86 }
87
88 /*
89  *  To make this work, we have to mess with the hero's mind.  The rules for
90  *  ball&chain are:
91  *
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.
96  *
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.
100  *
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.
109  */
110
111 /*
112  * from you.h
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
119  *
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
122  * Blind.
123  */
124
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 */
129
130 /*
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.
135  *
136  *  Should not be called while swallowed except on waterlevel.
137  */
138 STATIC_OVL void
139 placebc_core()
140 {
141     if (!uchain || !uball) {
142         impossible("Where are your ball and chain?");
143         return;
144     }
145
146     (void) flooreffects(uchain, u.ux, u.uy, ""); /* chain might rust */
147
148     if (carried(uball)) { /* the ball is carried */
149         u.bc_order = BCPOS_DIFFER;
150     } else {
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;
155     }
156
157     place_object(uchain, u.ux, u.uy);
158
159     u.bglyph = u.cglyph = levl[u.ux][u.uy].glyph; /* pick up glyph */
160
161     newsym(u.ux, u.uy);
162     bcrestriction = 0;
163 }
164
165 STATIC_OVL void
166 unplacebc_core()
167 {
168     if (u.uswallow) {
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.
173              */
174             if (!carried(uball))
175                 obj_extract_self(uball);
176             obj_extract_self(uchain);
177         }
178         /* ball&chain not unplaced while swallowed */
179         return;
180     }
181
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;
186
187         newsym(uball->ox, uball->oy);
188     }
189     obj_extract_self(uchain);
190     if (Blind && (u.bc_felt & BC_CHAIN)) /* drop glyph */
191         levl[uchain->ox][uchain->oy].glyph = u.cglyph;
192
193     newsym(uchain->ox, uchain->oy);
194     u.bc_felt = 0; /* feel nothing */
195 }
196
197 STATIC_OVL boolean
198 check_restriction(restriction)
199 int restriction;
200 {
201     boolean ret = FALSE;
202
203     if (!bcrestriction || (restriction == override_restriction))
204         ret = TRUE;
205     else
206         ret = (bcrestriction == restriction) ? TRUE : FALSE;
207     return ret;
208 }
209
210 #ifndef BREADCRUMBS
211 void
212 placebc()
213 {
214     if (!check_restriction(0)) {
215 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
216         char panicbuf[BUFSZ];
217
218         Sprintf(panicbuf, "placebc denied, restriction in effect");
219         paniclog("placebc", panicbuf);
220 #endif
221         return;
222     }
223     if (uchain && uchain->where != OBJ_FREE) {
224         impossible("bc already placed?");
225         return;
226     }
227     placebc_core();
228 }
229
230 void
231 unplacebc()
232 {
233     if (bcrestriction) {
234         impossible("unplacebc denied, restriction in place");
235         return;
236     }
237     unplacebc_core();
238 }
239
240 int
241 unplacebc_and_covet_placebc()
242 {
243     int restriction = 0;
244
245     if (bcrestriction) {
246         impossible("unplacebc_and_covet_placebc denied, already restricted");
247     } else {
248         restriction = bcrestriction = rnd(400);
249         unplacebc_core();
250     }
251     return restriction;
252 }
253
254 void
255 lift_covet_and_placebc(pin)
256 int pin;
257 {
258     if (!check_restriction(pin)) {
259 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
260         char panicbuf[BUFSZ];
261
262         Sprintf(panicbuf, "lift_covet_and_placebc denied, %s",
263                 (pin != bcrestriction) ? "pin mismatch"
264                                        : "restriction in effect");
265         paniclog("placebc", panicbuf);
266 #endif
267         return;
268     }
269     if (uchain && uchain->where != OBJ_FREE) {
270         impossible("bc already placed?");
271         return;
272     }
273     placebc_core();
274 }
275
276 #else  /* BREADCRUMBS */
277
278 void
279 Placebc(funcnm, linenum)
280 const char *funcnm;
281 int linenum;
282 {
283     if (!check_restriction(0)) {
284 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
285         char panicbuf[BUFSZ];
286
287         Sprintf(panicbuf, "Placebc denied to %s:%d, restricted by %s:%d",
288                 funcnm, linenum,
289                 bcpbreadcrumbs.funcnm, bcpbreadcrumbs.linenum);
290         paniclog("Placebc", panicbuf);
291 #endif
292         return;
293     }
294     if ((uchain && uchain->where != OBJ_FREE)
295                    && bcpbreadcrumbs.in_effect) {
296         impossible("Placebc collision at %s:%d, already placed by %s:%d",
297                    funcnm, linenum,
298                    bcpbreadcrumbs.funcnm, bcpbreadcrumbs.linenum);
299         return;
300     }
301     bcpbreadcrumbs.in_effect = TRUE;
302     bcubreadcrumbs.in_effect = FALSE;
303     bcpbreadcrumbs.funcnm = funcnm;
304     bcpbreadcrumbs.linenum = linenum;
305     placebc_core();
306 }
307
308 void
309 Unplacebc(funcnm, linenum)
310 const char *funcnm;
311 int linenum;
312 {
313
314     if (bcrestriction) {
315         char panicbuf[BUFSZ];
316
317         Sprintf(panicbuf, "Unplacebc from %s:%d, when restricted to %s:%d",
318                 funcnm, linenum,
319                 bcubreadcrumbs.funcnm, bcubreadcrumbs.linenum);
320         paniclog("Unplacebc", panicbuf);
321     }
322     bcpbreadcrumbs.in_effect = FALSE;
323     bcubreadcrumbs.in_effect = TRUE;
324     bcubreadcrumbs.funcnm = funcnm;
325     bcubreadcrumbs.linenum = linenum;
326     unplacebc_core();
327 }
328
329 int
330 Unplacebc_and_covet_placebc(funcnm, linenum)
331 const char *funcnm;
332 int linenum;
333 {
334     int restriction = 0;
335
336     if (bcrestriction) {
337         impossible(
338           "Unplacebc_and_covet_placebc denied to %s:%d, restricted by %s:%d",
339                    funcnm, linenum,
340                    bcubreadcrumbs.funcnm, bcubreadcrumbs.linenum);
341     } else {
342         restriction = bcrestriction = rnd(400);
343         bcpbreadcrumbs.in_effect = FALSE;
344         bcubreadcrumbs.in_effect = TRUE;
345         bcubreadcrumbs.funcnm = funcnm;
346         bcubreadcrumbs.linenum = linenum;
347         unplacebc_core();
348     }
349     return restriction;
350 }
351
352 void
353 Lift_covet_and_placebc(pin, funcnm, linenum)
354 int pin;
355 char *funcnm;
356 int linenum;
357 {
358     if (!check_restriction(pin)) {
359 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
360         char panicbuf[BUFSZ];
361
362         Sprintf(panicbuf,
363                 "Lift_covet_and_placebc denied to %s:%d, restricted by %s:%d",
364                 funcnm, linenum,
365                 bcpbreadcrumbs.funcnm, bcpbreadcrumbs.linenum);
366         paniclog("Lift_covet_and_placebc", panicbuf);
367 #endif
368         return;
369     }
370     if (uchain && uchain->where != OBJ_FREE) {
371         impossible("bc already placed?");
372         return;
373     }
374     placebc_core();
375 }
376 #endif /* BREADCRUMBS */
377
378 /*
379  *  Return the stacking of the hero's ball & chain.  This assumes that the
380  *  hero is being punished.
381  */
382 STATIC_OVL int
383 bc_order()
384 {
385     struct obj *obj;
386
387     if (uchain->ox != uball->ox || uchain->oy != uball->oy || carried(uball)
388         || u.uswallow)
389         return BCPOS_DIFFER;
390
391     for (obj = level.objects[uball->ox][uball->oy]; obj;
392          obj = obj->nexthere) {
393         if (obj == uchain)
394             return BCPOS_CHAIN;
395         if (obj == uball)
396             return BCPOS_BALL;
397     }
398     impossible("bc_order:  ball&chain not in same location!");
399     return BCPOS_DIFFER;
400 }
401
402 /*
403  *  set_bc()
404  *
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".
407  */
408 void
409 set_bc(already_blind)
410 int already_blind;
411 {
412     int ball_on_floor = !carried(uball);
413
414     u.bc_order = bc_order(); /* get the order */
415     u.bc_felt = ball_on_floor ? BC_BALL | BC_CHAIN : BC_CHAIN; /* felt */
416
417     if (already_blind || u.uswallow) {
418         u.cglyph = u.bglyph = levl[u.ux][u.uy].glyph;
419         return;
420     }
421
422     /*
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.
426      */
427     remove_object(uchain);
428     if (ball_on_floor)
429         remove_object(uball);
430
431     newsym(uchain->ox, uchain->oy);
432     u.cglyph = levl[uchain->ox][uchain->oy].glyph;
433
434     if (u.bc_order == BCPOS_DIFFER) { /* different locations */
435         place_object(uchain, uchain->ox, uchain->oy);
436         newsym(uchain->ox, uchain->oy);
437         if (ball_on_floor) {
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 */
442         }
443     } else {
444         u.bglyph = u.cglyph;
445         if (u.bc_order == BCPOS_CHAIN) {
446             place_object(uball, uball->ox, uball->oy);
447             place_object(uchain, uchain->ox, uchain->oy);
448         } else {
449             place_object(uchain, uchain->ox, uchain->oy);
450             place_object(uball, uball->ox, uball->oy);
451         }
452         newsym(uball->ox, uball->oy);
453     }
454 }
455
456 /*
457  *  move_bc()
458  *
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.
463  *
464  *  Should not be called while swallowed.
465  */
466 void
467 move_bc(before, control, ballx, bally, chainx, chainy)
468 int before, control;
469 xchar ballx, bally, chainx, chainy; /* only matter !before */
470 {
471     if (Blind) {
472         /*
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
480          *  act accordingly.
481          */
482         if (!before) {
483             if ((control & BC_CHAIN) && (control & BC_BALL)) {
484                 /*
485                  *  Both ball and chain moved.  If felt, drop glyph.
486                  */
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;
491                 u.bc_felt = 0;
492
493                 /* Pick up glyph at new location. */
494                 u.bglyph = levl[ballx][bally].glyph;
495                 u.cglyph = levl[chainx][chainy].glyph;
496
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);
506                         } else {
507                             levl[uball->ox][uball->oy].glyph = u.bglyph;
508                         }
509                     }
510                     u.bc_felt &= ~BC_BALL; /* no longer feel the ball */
511                 }
512
513                 /* Pick up glyph at new position. */
514                 u.bglyph = (ballx != chainx || bally != chainy)
515                                ? levl[ballx][bally].glyph
516                                : u.cglyph;
517
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);
526                         } else {
527                             levl[uchain->ox][uchain->oy].glyph = u.cglyph;
528                         }
529                     }
530                     u.bc_felt &= ~BC_CHAIN;
531                 }
532                 /* Pick up glyph at new position. */
533                 u.cglyph = (ballx != chainx || bally != chainy)
534                                ? levl[chainx][chainy].glyph
535                                : u.bglyph;
536
537                 movobj(uchain, chainx, chainy);
538             }
539
540             u.bc_order = bc_order(); /* reset the order */
541         }
542
543     } else {
544         /*
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.
548          */
549         if (before) {
550             if (!control) {
551                 /*
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.
555                  */
556                 u.bc_order = bc_order();
557             }
558
559             remove_object(uchain);
560             newsym(uchain->ox, uchain->oy);
561             if (!carried(uball)) {
562                 remove_object(uball);
563                 newsym(uball->ox, uball->oy);
564             }
565         } else {
566             int on_floor = !carried(uball);
567
568             if ((control & BC_CHAIN)
569                 || (!control && u.bc_order == BCPOS_CHAIN)) {
570                 /* If the chain moved or nothing moved & chain on top. */
571                 if (on_floor)
572                     place_object(uball, ballx, bally);
573                 place_object(uchain, chainx, chainy); /* chain on top */
574             } else {
575                 place_object(uchain, chainx, chainy);
576                 if (on_floor)
577                     place_object(uball, ballx, bally);
578                 /* ball on top */
579             }
580             newsym(chainx, chainy);
581             if (on_floor)
582                 newsym(ballx, bally);
583         }
584     }
585 }
586
587 /* return TRUE if the caller needs to place the ball and chain down again */
588 boolean
589 drag_ball(x, y, bc_control, ballx, bally, chainx, chainy, cause_delay,
590           allow_drag)
591 xchar x, y;
592 int *bc_control;
593 xchar *ballx, *bally, *chainx, *chainy;
594 boolean *cause_delay;
595 boolean allow_drag;
596 {
597     struct trap *t = (struct trap *) 0;
598     boolean already_in_rock;
599
600     /*
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.
604      *
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.
608      *
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.
612      */
613
614     *ballx = uball->ox;
615     *bally = uball->oy;
616     *chainx = uchain->ox;
617     *chainy = uchain->oy;
618     *bc_control = 0;
619     *cause_delay = FALSE;
620
621     if (dist2(x, y, uchain->ox, uchain->oy) <= 2) { /* nothing moved */
622         move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
623         return TRUE;
624     }
625
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;
629
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) {
635                 *chainx = u.ux;
636                 *chainy = u.uy;
637             }
638             return TRUE;
639         }
640
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))))
648     /*
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.
654      */
655 #define SKIP_TO_DRAG \
656     do {                                                           \
657         *chainx = oldchainx;                                       \
658         *chainy = oldchainy;                                       \
659         move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \
660         goto drag;                                                 \
661     } while (0)
662
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;
666         else
667             already_in_rock = FALSE;
668
669         switch (dist2(x, y, uball->ox, uball->oy)) {
670         /* two spaces diagonal from ball, move chain inbetween */
671         case 8:
672             *chainx = (uball->ox + x) / 2;
673             *chainy = (uball->oy + y) / 2;
674             if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
675                 SKIP_TO_DRAG;
676             break;
677
678         /* player is distance 2/1 from ball; move chain to one of the
679          * two spaces between
680          *   @
681          *   __
682          *    0
683          */
684         case 5: {
685             xchar tempx, tempy, tempx2, tempy2;
686
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) {
690                 tempx = x;
691                 tempx2 = uball->ox;
692                 tempy = tempy2 = (uball->oy + y) / 2;
693             } else {
694                 tempx = tempx2 = (uball->ox + x) / 2;
695                 tempy = y;
696                 tempy2 = uball->oy;
697             }
698             if (IS_CHAIN_ROCK(tempx, tempy) && !IS_CHAIN_ROCK(tempx2, tempy2)
699                 && !already_in_rock) {
700                 if (allow_drag) {
701                     /* Avoid pathological case *if* not teleporting:
702                      *   0                          0_
703                      *   _X  move northeast  ----->  X@
704                      *    @
705                      */
706                     if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5
707                         && dist2(x, y, tempx, tempy) == 1)
708                         SKIP_TO_DRAG;
709                     /* Avoid pathological case *if* not teleporting:
710                      *    0                          0
711                      *   _X  move east       ----->  X_
712                      *    @                           @
713                      */
714                     if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4
715                         && dist2(x, y, tempx, tempy) == 2)
716                         SKIP_TO_DRAG;
717                 }
718                 *chainx = tempx2;
719                 *chainy = tempy2;
720             } else if (!IS_CHAIN_ROCK(tempx, tempy)
721                        && IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
722                 if (allow_drag) {
723                     if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5
724                         && dist2(x, y, tempx2, tempy2) == 1)
725                         SKIP_TO_DRAG;
726                     if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4
727                         && dist2(x, y, tempx2, tempy2) == 2)
728                         SKIP_TO_DRAG;
729                 }
730                 *chainx = tempx;
731                 *chainy = tempy;
732             } else if (IS_CHAIN_ROCK(tempx, tempy)
733                        && IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
734                 SKIP_TO_DRAG;
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))
739                            && rn2(2))) {
740                 *chainx = tempx;
741                 *chainy = tempy;
742             } else {
743                 *chainx = tempx2;
744                 *chainy = tempy2;
745             }
746             break;
747         }
748
749         /* ball is two spaces horizontal or vertical from player; move*/
750         /* chain inbetween *unless* current chain position is OK */
751         case 4:
752             if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
753                 break;
754             *chainx = (x + uball->ox) / 2;
755             *chainy = (y + uball->oy) / 2;
756             if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
757                 SKIP_TO_DRAG;
758             break;
759
760         /* ball is one space diagonal from player.  Check for the
761          * following special case:
762          *   @
763          *    _    moving southwest becomes  @_
764          *   0                                0
765          * (This will also catch teleporting that happens to resemble
766          * this case, but oh well.)  Otherwise fall through.
767          */
768         case 2:
769             if (dist2(x, y, uball->ox, uball->oy) == 2
770                 && dist2(x, y, uchain->ox, uchain->oy) == 4) {
771                 if (uchain->oy == y)
772                     *chainx = uball->ox;
773                 else
774                     *chainy = uball->oy;
775                 if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
776                     SKIP_TO_DRAG;
777                 break;
778             }
779         /* fall through */
780         case 1:
781         case 0:
782             /* do nothing if possible */
783             if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
784                 break;
785             /* otherwise try to drag chain to player's old position */
786             if (CHAIN_IN_MIDDLE(u.ux, u.uy)) {
787                 *chainx = u.ux;
788                 *chainy = u.uy;
789                 break;
790             }
791             /* otherwise use player's new position (they must have
792                teleported, for this to happen) */
793             *chainx = x;
794             *chainy = y;
795             break;
796
797         default:
798             impossible("bad chain movement");
799             break;
800         }
801 #undef SKIP_TO_DRAG
802 #undef CHAIN_IN_MIDDLE
803         return TRUE;
804     }
805
806  drag:
807
808     if (near_capacity() > SLT_ENCUMBER && dist2(x, y, u.ux, u.uy) <= 2) {
809 #if 0 /*JP:T*/
810         You("cannot %sdrag the heavy iron ball.",
811             invent ? "carry all that and also " : "");
812 #else
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Ü" : "");
815 #endif
816         nomul(0);
817         return FALSE;
818     }
819
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)))) {
827         if (Levitation) {
828 /*JP
829             You_feel("a tug from the iron ball.");
830 */
831             You("\93S\8b\85\82É\88ø\82Á\82Ï\82ç\82ê\82½\81D");
832             if (t)
833                 t->tseen = 1;
834         } else {
835             struct monst *victim;
836
837 /*JP
838             You("are jerked back by the iron ball!");
839 */
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) {
842                 int tmp;
843                 int dieroll = rnd(20);
844
845                 tmp = -2 + Luck + find_mac(victim);
846                 tmp += omon_adj(victim, uball, TRUE);
847
848                 if (tmp >= dieroll)
849                     (void) hmon(victim, uball, HMON_DRAGGED, dieroll);
850                 else
851                     miss(xname(uball), victim);
852
853             } /* now check again in case mon died */
854             if (!m_at(uchain->ox, uchain->oy)) {
855                 u.ux = uchain->ox;
856                 u.uy = uchain->oy;
857                 newsym(u.ux0, u.uy0);
858             }
859             nomul(0);
860
861             *bc_control = BC_BALL;
862             move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
863             *ballx = uchain->ox;
864             *bally = uchain->oy;
865             move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy);
866             spoteffects(TRUE);
867             return FALSE;
868         }
869     }
870
871     *bc_control = BC_BALL | BC_CHAIN;
872
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
880          * behavior.
881          */
882         *ballx = *chainx = x;
883         *bally = *chainy = y;
884     } else {
885         xchar newchainx = u.ux, newchainy = u.uy;
886
887         /*
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.
893          */
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 */
900                 newchainx = u.ux;
901                 newchainy = u.uy;
902             }
903         }
904
905         *ballx = uchain->ox;
906         *bally = uchain->oy;
907         *chainx = newchainx;
908         *chainy = newchainy;
909     }
910 #undef IS_CHAIN_ROCK
911     *cause_delay = TRUE;
912     return TRUE;
913 }
914
915 /*
916  *  drop_ball()
917  *
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.
921  *
922  *  Should not be called while swallowed.
923  */
924 void
925 drop_ball(x, y)
926 xchar x, y;
927 {
928     if (Blind) {
929         /* get the order */
930         u.bc_order = bc_order();
931         /* pick up glyph */
932         u.bglyph = (u.bc_order) ? u.cglyph : levl[x][y].glyph;
933     }
934
935     if (x != u.ux || y != u.uy) {
936 /*JP
937         static const char *pullmsg = "The ball pulls you out of the %s!";
938 */
939         static const char *pullmsg = "\93S\8b\85\82Í%s\82©\82ç\82 \82È\82½\82ð\88ø\82Á\82Ï\82è\8fo\82µ\82½\81I";
940         struct trap *t;
941         long side;
942
943         if (u.utrap
944             && u.utraptype != TT_INFLOOR && u.utraptype != TT_BURIEDBALL) {
945             switch (u.utraptype) {
946             case TT_PIT:
947 /*JP
948                 pline(pullmsg, "pit");
949 */
950                 pline(pullmsg, "\97\8e\82µ\8c\8a");
951                 break;
952             case TT_WEB:
953 /*JP
954                 pline(pullmsg, "web");
955 */
956                 pline(pullmsg, "\82­\82à\82Ì\91\83");
957 /*JP
958                 pline_The("web is destroyed!");
959 */
960                 pline("\82­\82à\82Ì\91\83\82Í\82±\82í\82ê\82½\81I");
961                 deltrap(t_at(u.ux, u.uy));
962                 break;
963             case TT_LAVA:
964 /*JP
965                 pline(pullmsg, hliquid("lava"));
966 */
967                 pline(pullmsg, hliquid("\97n\8aâ"));
968                 break;
969             case TT_BEARTRAP:
970                 side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
971 /*JP
972                 pline(pullmsg, "bear trap");
973 */
974                 pline(pullmsg, "\8cF\82Ìã©");
975                 set_wounded_legs(side, rn1(1000, 500));
976                 if (!u.usteed) {
977 #if 0 /*JP:T*/
978                     Your("%s %s is severely damaged.",
979                          (side == LEFT_SIDE) ? "left" : "right",
980                          body_part(LEG));
981 #else
982                     Your("%s%s\82Í\82Ð\82Ç\82¢\8f\9d\82ð\95\89\82Á\82½\81D",
983                          (side == LEFT_SIDE) ? "\8d¶" : "\89E",
984                          body_part(LEG));
985 #endif
986 #if 0 /*JP:T*/
987                     losehp(Maybe_Half_Phys(2),
988                            "leg damage from being pulled out of a bear trap",
989                            KILLED_BY);
990 #else
991                     losehp(Maybe_Half_Phys(2),
992                            "\8cF\82Ìã©\82©\82ç\94²\82¯\82æ\82¤\82Æ\91«\82ð\88ø\82Á\82Ï\82Á\82Ä",
993                            KILLED_BY);
994 #endif
995                 }
996                 break;
997             }
998             reset_utrap(TRUE);
999             fill_pit(u.ux, u.uy);
1000         }
1001
1002         u.ux0 = u.ux;
1003         u.uy0 = u.uy;
1004         if (!Levitation && !MON_AT(x, y) && !u.utrap
1005             && (is_pool(x, y)
1006                 || ((t = t_at(x, y))
1007                     && (is_pit(t->ttyp)
1008                         || is_hole(t->ttyp))))) {
1009             u.ux = x;
1010             u.uy = y;
1011         } else {
1012             u.ux = x - u.dx;
1013             u.uy = y - u.dy;
1014         }
1015         vision_full_recalc = 1; /* hero has moved, recalculate vision later */
1016
1017         if (Blind) {
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;
1024         }
1025         movobj(uchain, u.ux, u.uy); /* has a newsym */
1026         if (Blind) {
1027             u.bc_order = bc_order();
1028         }
1029         newsym(u.ux0, u.uy0); /* clean up old position */
1030         if (u.ux0 != u.ux || u.uy0 != u.uy) {
1031             spoteffects(TRUE);
1032             sokoban_guilt();
1033         }
1034     }
1035 }
1036
1037 /* ball&chain cause hero to randomly lose stuff from inventory */
1038 STATIC_OVL void
1039 litter()
1040 {
1041     struct obj *otmp, *nextobj = 0;
1042     int capacity = weight_cap();
1043
1044     for (otmp = invent; otmp; otmp = nextobj) {
1045         nextobj = otmp->nobj;
1046         if ((otmp != uball) && (rnd(capacity) <= (int) otmp->owt)) {
1047             if (canletgo(otmp, "")) {
1048 #if 0 /*JP:T*/
1049                 You("drop %s and %s %s down the stairs with you.",
1050                     yname(otmp), (otmp->quan == 1L) ? "it" : "they",
1051                     otense(otmp, "fall"));
1052 #else
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",
1054                     yname(otmp));
1055 #endif
1056                 dropx(otmp);
1057                 encumber_msg(); /* drop[xyz]() probably ought to to this... */
1058             }
1059         }
1060     }
1061 }
1062
1063 void
1064 drag_down()
1065 {
1066     boolean forward;
1067     uchar dragchance = 3;
1068
1069     /*
1070      *  Assume that the ball falls forward if:
1071      *
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
1076      */
1077     forward = carried(uball) && (uwep == uball || !uwep || !rn2(3));
1078
1079     if (carried(uball))
1080 /*JP
1081         You("lose your grip on the iron ball.");
1082 */
1083         You("\93S\8b\85\82ð\8eè\82©\82ç\97\8e\82µ\82Ä\82µ\82Ü\82Á\82½\81D");
1084
1085     cls();  /* previous level is still displayed although you
1086                went down the stairs. Avoids bug C343-20 */
1087
1088     if (forward) {
1089         if (rn2(6)) {
1090 /*JP
1091             pline_The("iron ball drags you downstairs!");
1092 */
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)),
1095 /*JP
1096                    "dragged downstairs by an iron ball", NO_KILLER_PREFIX);
1097 */
1098                    "\93S\8b\85\82É\82æ\82è\8aK\92i\82ð\82±\82ë\82ª\82è\97\8e\82¿\82Ä", KILLED_BY);
1099             litter();
1100         }
1101     } else {
1102         if (rn2(2)) {
1103 /*JP
1104             pline_The("iron ball smacks into you!");
1105 */
1106             pline("\93S\8b\85\82Í\82 \82È\82½\82É\83S\83c\83\93\82Æ\82Ô\82Â\82©\82Á\82½\81I");
1107 /*JP
1108             losehp(Maybe_Half_Phys(rnd(20)), "iron ball collision",
1109 */
1110             losehp(Maybe_Half_Phys(rnd(20)), "\93S\8b\85\82Ì\8fÕ\93Ë\82Å",
1111                    KILLED_BY_AN);
1112             exercise(A_STR, FALSE);
1113             dragchance -= 2;
1114         }
1115         if ((int) dragchance >= rnd(6)) {
1116 /*JP
1117             pline_The("iron ball drags you downstairs!");
1118 */
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)),
1121 /*JP
1122                    "dragged downstairs by an iron ball", NO_KILLER_PREFIX);
1123 */
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);
1126             litter();
1127         }
1128     }
1129 }
1130
1131 void
1132 bc_sanity_check()
1133 {
1134     int otyp, freeball, freechain;
1135     const char *onam;
1136
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" : "");
1147     }
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)) {
1162         otyp = uball->otyp;
1163         onam = safe_typename(otyp);
1164         impossible("uball: type %d (%s), where %d, wornmask=0x%08lx",
1165                    otyp, onam, uball->where, uball->owornmask);
1166     }
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);
1179     }
1180     if (uball && uchain && !(freeball && freechain)) {
1181         int bx, by, cx, cy, bdx, bdy, cdx, cdy;
1182
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() */
1190         else
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)
1195             impossible(
1196                      "b&c distance: you@<%d,%d>, chain@<%d,%d>, ball@<%d,%d>",
1197                        u.ux, u.uy, cx, cy, bx, by);
1198     }
1199     /* [check bc_order too?] */
1200 }
1201
1202 /*ball.c*/