OSDN Git Service

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