Source:NetHack 3.4.3/src/ball.c

From NetHackWiki
(Redirected from Source:NetHack 3.4.3/ball.c)
Jump to: navigation, search

Below is the full text to src/ball.c from NetHack 3.4.3. To link to a particular line, write [[ball.c#line123]], for example.

Top of file

  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. */

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

  1. /* Ball & Chain =============================================================*/
  2.  
  3. #include "hack.h"
  4.  
  5. STATIC_DCL int NDECL(bc_order);
  6. STATIC_DCL void NDECL(litter);

ballfall

  1. void
  2. ballfall()
  3. {
  4. 	boolean gets_hit;
  5.  
  6. 	gets_hit = (((uball->ox != u.ux) || (uball->oy != u.uy)) &&
  7. 		    ((uwep == uball)? FALSE : (boolean)rn2(5)));
  8. 	if (carried(uball)) {
  9. 		pline("Startled, you drop the iron ball.");
  10. 		if (uwep == uball)
  11. 			setuwep((struct obj *)0);
  12. 		if (uswapwep == uball)
  13. 			setuswapwep((struct obj *)0);
  14. 		if (uquiver == uball)
  15. 			setuqwep((struct obj *)0);;
  16. 		if (uwep != uball)
  17. 			freeinv(uball);
  18. 	}
  19. 	if(gets_hit){
  20. 		int dmg = rn1(7,25);
  21. 		pline_The("iron ball falls on your %s.",
  22. 			body_part(HEAD));
  23. 		if (uarmh) {
  24. 		    if(is_metallic(uarmh)) {
  25. 			pline("Fortunately, you are wearing a hard helmet.");
  26. 			dmg = 3;
  27. 		    } else if (flags.verbose)
  28. 			Your("%s does not protect you.", xname(uarmh));
  29. 		}
  30. 		losehp(dmg, "crunched in the head by an iron ball",
  31. 			NO_KILLER_PREFIX);
  32. 	}
  33. }

Ballfall checks whether the user is carrying the iron ball. If so, it removes it from the player's inventory.

If the user is hit by the iron ball, ballfall checks whether the user is wearing a metallic helmet. If so, the user is only injured for 3. Otherwise, the user is injured for a random number from 25 to 32.

This function is called when the player falls into a pit, spiked pit, (goto_level in do.c line 1210) or trap door (dotrap in trap.c line 933).

Explanation of ball and chain mechanics

  1. /*
  2. *  To make this work, we have to mess with the hero's mind.  The rules for
  3. *  ball&chain are:
  4. *
  5. *	1. If the hero can see them, fine.
  6. *	2. If the hero can't see either, it isn't seen.
  7. *	3. If either is felt it is seen.
  8. *	4. If either is felt and moved, it disappears.
  9. *
  10. *  If the hero can see, then when a move is done, the ball and chain are
  11. *  first picked up, the positions under them are corrected, then they
  12. *  are moved after the hero moves.  Not too bad.
  13. *
  14. *  If the hero is blind, then she can "feel" the ball and/or chain at any
  15. *  time.  However, when the hero moves, the felt ball and/or chain become
  16. *  unfelt and whatever was felt "under" the ball&chain appears.  Pretty
  17. *  nifty, but it requires that the ball&chain "remember" what was under
  18. *  them --- i.e. they pick-up glyphs when they are felt and drop them when
  19. *  moved (and felt).  When swallowed, the ball&chain are pulled completely
  20. *  off of the dungeon, but are still on the object chain.  They are placed
  21. *  under the hero when she is expelled.
  22. */
  23.  
  24. /*
  25. * from you.h
  26. *	int u.bglyph		glyph under the ball
  27. *	int u.cglyph		glyph under the chain
  28. *	int u.bc_felt		mask for ball/chain being felt
  29. *	#define BC_BALL  0x01	bit mask in u.bc_felt for ball
  30. *	#define BC_CHAIN 0x02	bit mask in u.bc_felt for chain
  31. *	int u.bc_order		ball & chain order
  32. *
  33. * u.bc_felt is also manipulated in display.c and read.c, the others only
  34. * in this file.  None of these variables are valid unless the player is
  35. * Blind.
  36. */
  37.  
  38. /* values for u.bc_order */
  39. #define BCPOS_DIFFER	0	/* ball & chain at different positions */
  40. #define BCPOS_CHAIN	1	/* chain on top of ball */
  41. #define BCPOS_BALL	2	/* ball on top of chain */

Even the documentation is confusing. Let's try translating some of this Zen: if you use step on the ball/chain or search for it the glyph will appear on screen, covering up the glyph of whatever other item or feature is there. (Your character felt it, so you know it's there.) BUT, if you then move, then your character knows the ball and chain moved too, so the glyphs disappear. The glyphs that were there before your character felt the ball and chain are restored.

placebc

  1. /*
  2. *  Place the ball & chain under the hero.  Make sure that the ball & chain
  3. *  variables are set (actually only needed when blind, but what the heck).
  4. *  It is assumed that when this is called, the ball and chain are NOT
  5. *  attached to the object list.
  6. *
  7. *  Should not be called while swallowed.
  8. */
  9. void
  10. placebc()
  11. {
  12. if (!uchain || !uball) {
  13. 	impossible("Where are your ball and chain?");
  14. 	return;
  15. }
  16.  
  17. (void) flooreffects(uchain, u.ux, u.uy, "");	/* chain might rust */
  18.  
  19. if (carried(uball))		/* the ball is carried */
  20. 	u.bc_order = BCPOS_DIFFER;
  21. else {
  22. 	/* ball might rust -- already checked when carried */
  23. 	(void) flooreffects(uball, u.ux, u.uy, "");
  24. 	place_object(uball, u.ux, u.uy);
  25. 	u.bc_order = BCPOS_CHAIN;
  26. }
  27.  
  28. place_object(uchain, u.ux, u.uy);
  29.  
  30. u.bglyph = u.cglyph = levl[u.ux][u.uy].glyph;   /* pick up glyph */
  31.  
  32. newsym(u.ux,u.uy);
  33. }

placebc places the ball and chain under the player, if the player has a ball and chain. If the floor has any special properties, they might cause the ball and chain to rust. Also, the user picks up a glyph.

A new symbol is then generated for the current location.

See unplacebc and its annotations.

unplacebc

  1. void
  2. unplacebc()
  3. {
  4. if (u.uswallow) return;	/* ball&chain not placed while swallowed */
  5.  
  6. if (!carried(uball)) {
  7. 	obj_extract_self(uball);
  8. 	if (Blind && (u.bc_felt & BC_BALL))		/* drop glyph */
  9. 	    levl[uball->ox][uball->oy].glyph = u.bglyph;
  10.  
  11. 	newsym(uball->ox,uball->oy);
  12. }
  13. obj_extract_self(uchain);
  14. if (Blind && (u.bc_felt & BC_CHAIN))		/* drop glyph */
  15. 	levl[uchain->ox][uchain->oy].glyph = u.cglyph;
  16.  
  17. newsym(uchain->ox,uchain->oy);
  18. u.bc_felt = 0;					/* feel nothing */
  19. }

The ball and chain are being moved, so we need to put down the old glyphs. Also, we need to free the ball and chain from their old object lists. This function is NOT called to remove the ball and chain: unpunish in read.c is responsible for that.

placebc and unplacebc are NOT used for normal movement, drag_ball in ball.c (below) is responsible for that. These functions are used for a series of special cases that involve instantaneous movement of the ball and chain:

bc_order

  1. /*
  2. *  Return the stacking of the hero's ball & chain.  This assumes that the
  3. *  hero is being punished.
  4. */
  5. STATIC_OVL int
  6. bc_order()
  7. {
  8. struct obj *obj;
  9.  
  10. if (uchain->ox != uball->ox || uchain->oy != uball->oy || carried(uball)
  11. 		|| u.uswallow)
  12. 	return BCPOS_DIFFER;
  13.  
  14. for (obj = level.objects[uball->ox][uball->oy]; obj; obj = obj->nexthere) {
  15. 	if (obj == uchain) return BCPOS_CHAIN;
  16. 	if (obj == uball) return BCPOS_BALL;
  17. }
  18. impossible("bc_order:  ball&chain not in same location!");
  19. return BCPOS_DIFFER;
  20. }

set_bc

  1. /*
  2. *  set_bc()
  3. *
  4. *  The hero is either about to go blind or already blind and just punished.
  5. *  Set up the ball and chain variables so that the ball and chain are "felt".
  6. */
  7. void
  8. set_bc(already_blind)
  9. int already_blind;
  10. {
  11. int ball_on_floor = !carried(uball);
  12.  
  13. u.bc_order = bc_order();				/* get the order */
  14. u.bc_felt = ball_on_floor ? BC_BALL|BC_CHAIN : BC_CHAIN;	/* felt */
  15.  
  16. if (already_blind || u.uswallow) {
  17. 	u.cglyph = u.bglyph = levl[u.ux][u.uy].glyph;
  18. 	return;
  19. }
  20.  
  21. /*
  22. *  Since we can still see, remove the ball&chain and get the glyph that
  23. *  would be beneath them.  Then put the ball&chain back.  This is pretty
  24. *  disgusting, but it will work.
  25. */
  26. remove_object(uchain);
  27. if (ball_on_floor) remove_object(uball);
  28.  
  29. newsym(uchain->ox, uchain->oy);
  30. u.cglyph = levl[uchain->ox][uchain->oy].glyph;
  31.  
  32. if (u.bc_order == BCPOS_DIFFER) {		/* different locations */
  33. 	place_object(uchain, uchain->ox, uchain->oy);
  34. 	newsym(uchain->ox, uchain->oy);
  35. 	if (ball_on_floor) {
  36. 	    newsym(uball->ox, uball->oy);		/* see under ball */
  37. 	    u.bglyph = levl[uball->ox][uball->oy].glyph;
  38. 	    place_object(uball,  uball->ox, uball->oy);
  39. 	    newsym(uball->ox, uball->oy);		/* restore ball */
  40. 	}
  41. } else {
  42. 	u.bglyph = u.cglyph;
  43. 	if (u.bc_order == BCPOS_CHAIN) {
  44. 	    place_object(uball,  uball->ox, uball->oy);
  45. 	    place_object(uchain, uchain->ox, uchain->oy);
  46. 	} else {
  47. 	    place_object(uchain, uchain->ox, uchain->oy);
  48. 	    place_object(uball,  uball->ox, uball->oy);
  49. 	}
  50. 	newsym(uball->ox, uball->oy);
  51. }
  52. }

move_bc

  1. /*
  2. *  move_bc()
  3. *
  4. *  Move the ball and chain.  This is called twice for every move.  The first
  5. *  time to pick up the ball and chain before the move, the second time to
  6. *  place the ball and chain after the move.  If the ball is carried, this
  7. *  function should never have BC_BALL as part of its control.
  8. *
  9. *  Should not be called while swallowed.
  10. */
  11. void
  12. move_bc(before, control, ballx, bally, chainx, chainy)
  13. int   before, control;
  14. xchar ballx, bally, chainx, chainy;	/* only matter !before */
  15. {
  16. if (Blind) {
  17. 	/*
  18. 	 *  The hero is blind.  Time to work hard.  The ball and chain that
  19. 	 *  are attached to the hero are very special.  The hero knows that
  20. 	 *  they are attached, so when they move, the hero knows that they
  21. 	 *  aren't at the last position remembered.  This is complicated
  22. 	 *  by the fact that the hero can "feel" the surrounding locations
  23. 	 *  at any time, hence, making one or both of them show up again.
  24. 	 *  So, we have to keep track of which is felt at any one time and
  25. 	 *  act accordingly.
  26. 	 */
  27. 	if (!before) {
  28. 	    if ((control & BC_CHAIN) && (control & BC_BALL)) {
  29. 		/*
  30. 		 *  Both ball and chain moved.  If felt, drop glyph.
  31. 		 */
  32. 		if (u.bc_felt & BC_BALL)
  33. 		    levl[uball->ox][uball->oy].glyph = u.bglyph;
  34. 		if (u.bc_felt & BC_CHAIN)
  35. 		    levl[uchain->ox][uchain->oy].glyph = u.cglyph;
  36. 		u.bc_felt = 0;
  37.  
  38. 		/* Pick up glyph at new location. */
  39. 		u.bglyph = levl[ballx][bally].glyph;
  40. 		u.cglyph = levl[chainx][chainy].glyph;
  41.  
  42. 		movobj(uball,ballx,bally);
  43. 		movobj(uchain,chainx,chainy);
  44. 	    } else if (control & BC_BALL) {
  45. 		if (u.bc_felt & BC_BALL) {
  46. 		    if (u.bc_order == BCPOS_DIFFER) {	/* ball by itself */
  47. 			levl[uball->ox][uball->oy].glyph = u.bglyph;
  48. 		    } else if (u.bc_order == BCPOS_BALL) {
  49. 			if (u.bc_felt & BC_CHAIN) {   /* know chain is there */
  50. 			    map_object(uchain, 0);
  51. 			} else {
  52. 			    levl[uball->ox][uball->oy].glyph = u.bglyph;
  53. 			}
  54. 		    }
  55. 		    u.bc_felt &= ~BC_BALL;	/* no longer feel the ball */
  56. 		}
  57.  
  58. 		/* Pick up glyph at new position. */
  59. 		u.bglyph = (ballx != chainx || bally != chainy) ?
  60. 					levl[ballx][bally].glyph : u.cglyph;
  61.  
  62. 		movobj(uball,ballx,bally);
  63. 	    } else if (control & BC_CHAIN) {
  64. 		if (u.bc_felt & BC_CHAIN) {
  65. 		    if (u.bc_order == BCPOS_DIFFER) {
  66. 			levl[uchain->ox][uchain->oy].glyph = u.cglyph;
  67. 		    } else if (u.bc_order == BCPOS_CHAIN) {
  68. 			if (u.bc_felt & BC_BALL) {
  69. 			    map_object(uball, 0);
  70. 			} else {
  71. 			    levl[uchain->ox][uchain->oy].glyph = u.cglyph;
  72. 			}
  73. 		    }
  74. 		    u.bc_felt &= ~BC_CHAIN;
  75. 		}
  76. 		/* Pick up glyph at new position. */
  77. 		u.cglyph = (ballx != chainx || bally != chainy) ?
  78. 					levl[chainx][chainy].glyph : u.bglyph;
  79.  
  80. 		movobj(uchain,chainx,chainy);
  81. 	    }
  82.  
  83. 	    u.bc_order = bc_order();	/* reset the order */
  84. 	}
  85.  
  86. } else {
  87. 	/*
  88. 	 *  The hero is not blind.  To make this work correctly, we need to
  89. 	 *  pick up the ball and chain before the hero moves, then put them
  90. 	 *  in their new positions after the hero moves.
  91. 	 */
  92. 	if (before) {
  93. 	    if (!control) {
  94. 		/*
  95. 		 * Neither ball nor chain is moving, so remember which was
  96. 		 * on top until !before.  Use the variable u.bc_order
  97. 		 * since it is only valid when blind.
  98. 		 */
  99. 		u.bc_order = bc_order();
  100. 	    }
  101.  
  102. 	    remove_object(uchain);
  103. 	    newsym(uchain->ox, uchain->oy);
  104. 	    if (!carried(uball)) {
  105. 		remove_object(uball);
  106. 		newsym(uball->ox,  uball->oy);
  107. 	    }
  108. 	} else {
  109. 	    int on_floor = !carried(uball);
  110.  
  111. 	    if ((control & BC_CHAIN) ||
  112. 				(!control && u.bc_order == BCPOS_CHAIN)) {
  113. 		/* If the chain moved or nothing moved & chain on top. */
  114. 		if (on_floor) place_object(uball,  ballx, bally);
  115. 		place_object(uchain, chainx, chainy);	/* chain on top */
  116. 	    } else {
  117. 		place_object(uchain, chainx, chainy);
  118. 		if (on_floor) place_object(uball,  ballx, bally);
  119. 							    /* ball on top */
  120. 	    }
  121. 	    newsym(chainx, chainy);
  122. 	    if (on_floor) newsym(ballx, bally);
  123. 	}
  124. }
  125. }

move_bc is generally called by drag_ball. It is only ever called directly in some special cases.

drag_ball

  1. /* return TRUE if the caller needs to place the ball and chain down again
  2. *
  3. *  Should not be called while swallowed.  Should be called before movement,
  4. *  because we might want to move the ball or chain to the hero's old position.
  5. *
  6. * It is called if we are moving.  It is also called if we are teleporting
  7. * *if* the ball doesn't move and we thus must drag the chain.  It is not
  8. * called for ordinary teleportation.
  9. *
  10. * allow_drag is only used in the ugly special case where teleporting must
  11. * drag the chain, while an identical-looking movement must drag both the ball
  12. * and chain.
  13. */
  14. boolean
  15. drag_ball(x, y, bc_control, ballx, bally, chainx, chainy, cause_delay,
  16. allow_drag)
  17. xchar x, y;
  18. int *bc_control;
  19. xchar *ballx, *bally, *chainx, *chainy;
  20. boolean *cause_delay;
  21. boolean allow_drag;
  22. {
  23. 	struct trap *t = (struct trap *)0;
  24. 	boolean already_in_rock;
  25.  
  26. 	*ballx  = uball->ox;
  27. 	*bally  = uball->oy;
  28. 	*chainx = uchain->ox;
  29. 	*chainy = uchain->oy;
  30. 	*bc_control = 0;
  31. 	*cause_delay = FALSE;
  32.  
  33. 	if (dist2(x, y, uchain->ox, uchain->oy) <= 2) {	/* nothing moved */
  34. 	    move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
  35. 	    return TRUE;
  36. 	}
  37.  
  38. 	/* only need to move the chain? */
  39. 	if (carried(uball) || distmin(x, y, uball->ox, uball->oy) <= 2) {
  40. 	    xchar oldchainx = uchain->ox, oldchainy = uchain->oy;
  41. 	    *bc_control = BC_CHAIN;
  42. 	    move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
  43. 	    if (carried(uball)) {
  44. 		/* move chain only if necessary */
  45. 		if (distmin(x, y, uchain->ox, uchain->oy) > 1) {
  46. 		    *chainx = u.ux;
  47. 		    *chainy = u.uy;
  48. 		}
  49. 		return TRUE;
  50. 	    }
  51. #define CHAIN_IN_MIDDLE(chx, chy) \
  52. (distmin(x, y, chx, chy) <= 1 && distmin(chx, chy, uball->ox, uball->oy) <= 1)
  53. #define IS_CHAIN_ROCK(x,y) \
  54. (IS_ROCK(levl[x][y].typ) || (IS_DOOR(levl[x][y].typ) && \
  55. (levl[x][y].doormask & (D_CLOSED|D_LOCKED))))
  56. /* Don't ever move the chain into solid rock.  If we have to, then instead
  57. * undo the move_bc() and jump to the drag ball code.  Note that this also
  58. * means the "cannot carry and drag" message will not appear, since unless we
  59. * moved at least two squares there is no possibility of the chain position
  60. * being in solid rock.
  61. */
  62. #define SKIP_TO_DRAG { *chainx = oldchainx; *chainy = oldchainy; \
  63. move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \
  64. goto drag; } 
  65. 	    if (IS_CHAIN_ROCK(u.ux, u.uy) || IS_CHAIN_ROCK(*chainx, *chainy)
  66. 			|| IS_CHAIN_ROCK(uball->ox, uball->oy))
  67. 		already_in_rock = TRUE;
  68. 	    else
  69. 		already_in_rock = FALSE;
  70.  
  71. 	    switch(dist2(x, y, uball->ox, uball->oy)) {
  72. 		/* two spaces diagonal from ball, move chain inbetween */
  73. 		case 8:
  74. 		    *chainx = (uball->ox + x)/2;
  75. 		    *chainy = (uball->oy + y)/2;
  76. 		    if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
  77. 			SKIP_TO_DRAG;
  78. 		    break;
  79.  
  80. 		/* player is distance 2/1 from ball; move chain to one of the
  81. 		 * two spaces between
  82. 		 *   @
  83. 		 *   __
  84. 		 *    0
  85. 		 */
  86. 		case 5: {
  87. 		    xchar tempx, tempy, tempx2, tempy2;
  88.  
  89. 		    /* find position closest to current position of chain */
  90. 		    /* no effect if current position is already OK */
  91. 		    if (abs(x - uball->ox) == 1) {
  92. 			tempx = x;
  93. 			tempx2 = uball->ox;
  94. 			tempy = tempy2 = (uball->oy + y)/2;
  95. 		    } else {
  96. 			tempx = tempx2 = (uball->ox + x)/2;
  97. 			tempy = y;
  98. 			tempy2 = uball->oy;
  99. 		    }
  100. 		    if (IS_CHAIN_ROCK(tempx, tempy) &&
  101. 				!IS_CHAIN_ROCK(tempx2, tempy2) &&
  102. 				!already_in_rock) {
  103. 			if (allow_drag) {
  104. 			    /* Avoid pathological case *if* not teleporting:
  105. 			     *   0			    0_
  106. 			     *   _X  move northeast  ----->  X@
  107. 			     *    @
  108. 			     */
  109. 			    if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5 &&
  110. 				  dist2(x, y, tempx, tempy) == 1)
  111. 				SKIP_TO_DRAG;
  112. 			    /* Avoid pathological case *if* not teleporting:
  113. 			     *    0			     0
  114. 			     *   _X  move east       ----->  X_
  115. 			     *    @			      @
  116. 			     */
  117. 			    if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4 &&
  118. 				  dist2(x, y, tempx, tempy) == 2)
  119. 				SKIP_TO_DRAG;
  120. 			}
  121. 			*chainx = tempx2;
  122. 			*chainy = tempy2;
  123. 		    } else if (!IS_CHAIN_ROCK(tempx, tempy) &&
  124. 				IS_CHAIN_ROCK(tempx2, tempy2) &&
  125. 				!already_in_rock) {
  126. 			if (allow_drag) {
  127. 			    if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5 &&
  128. 				    dist2(x, y, tempx2, tempy2) == 1)
  129. 				SKIP_TO_DRAG;
  130. 			    if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4 &&
  131. 				  dist2(x, y, tempx2, tempy2) == 2)
  132. 				SKIP_TO_DRAG;
  133. 			}
  134. 			*chainx = tempx;
  135. 			*chainy = tempy;
  136. 		    } else if (IS_CHAIN_ROCK(tempx, tempy) &&
  137. 				IS_CHAIN_ROCK(tempx2, tempy2) &&
  138. 				!already_in_rock) {
  139. 			SKIP_TO_DRAG;
  140. 		    } else if (dist2(tempx, tempy, uchain->ox, uchain->oy) <
  141. 			 dist2(tempx2, tempy2, uchain->ox, uchain->oy) ||
  142. 		       ((dist2(tempx, tempy, uchain->ox, uchain->oy) ==
  143. 			 dist2(tempx2, tempy2, uchain->ox, uchain->oy)) && rn2(2))) {
  144. 			*chainx = tempx;
  145. 			*chainy = tempy;
  146. 		    } else {
  147. 			*chainx = tempx2;
  148. 			*chainy = tempy2;
  149. 		    }
  150. 		    break;
  151. 		}
  152.  
  153. 		/* ball is two spaces horizontal or vertical from player; move*/
  154. 		/* chain inbetween *unless* current chain position is OK */
  155. 		case 4:
  156. 		    if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
  157. 			break;
  158. 		    *chainx = (x + uball->ox)/2;
  159. 		    *chainy = (y + uball->oy)/2;
  160. 		    if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
  161. 			SKIP_TO_DRAG;
  162. 		    break;
  163.  
  164. 		/* ball is one space diagonal from player.  Check for the
  165. 		 * following special case:
  166. 		 *   @
  167. 		 *    _    moving southwest becomes  @_
  168. 		 *   0                                0
  169. 		 * (This will also catch teleporting that happens to resemble
  170. 		 * this case, but oh well.)  Otherwise fall through.
  171. 		 */
  172. 		case 2:
  173. 		    if (dist2(x, y, uball->ox, uball->oy) == 2 &&
  174. 			    dist2(x, y, uchain->ox, uchain->oy) == 4) {
  175. 			if (uchain->oy == y)
  176. 			    *chainx = uball->ox;
  177. 			else
  178. 			    *chainy = uball->oy;
  179. 			if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
  180. 			    SKIP_TO_DRAG;
  181. 			break;
  182. 		    }
  183. 		    /* fall through */
  184. 		case 1:
  185. 		case 0:
  186. 		    /* do nothing if possible */
  187. 		    if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
  188. 			break;
  189. 		    /* otherwise try to drag chain to player's old position */
  190. 		    if (CHAIN_IN_MIDDLE(u.ux, u.uy)) {
  191. 			*chainx = u.ux;
  192. 			*chainy = u.uy;
  193. 			break;
  194. 		    }
  195. 		    /* otherwise use player's new position (they must have
  196. 		       teleported, for this to happen) */
  197. 		    *chainx = x;
  198. 		    *chainy = y;
  199. 		    break;
  200.  
  201. 		default: impossible("bad chain movement");
  202. 		    break;
  203. 	    }
  204. #undef SKIP_TO_DRAG
  205. #undef IS_CHAIN_ROCK
  206. #undef CHAIN_IN_MIDDLE
  207. 	    return TRUE;
  208. 	}
  209.  
  210. drag:
  211.  
  212. 	if (near_capacity() > SLT_ENCUMBER && dist2(x, y, u.ux, u.uy) <= 2) {
  213. 	    You("cannot %sdrag the heavy iron ball.",
  214. 			    invent ? "carry all that and also " : "");
  215. 	    nomul(0);
  216. 	    return FALSE;
  217. 	}
  218.  
  219. 	if ((is_pool(uchain->ox, uchain->oy) &&
  220. 			/* water not mere continuation of previous water */
  221. 			(levl[uchain->ox][uchain->oy].typ == POOL ||
  222. 			 !is_pool(uball->ox, uball->oy) ||
  223. 			 levl[uball->ox][uball->oy].typ == POOL))
  224. 	    || ((t = t_at(uchain->ox, uchain->oy)) &&
  225. 			(t->ttyp == PIT ||
  226. 			 t->ttyp == SPIKED_PIT ||
  227. 			 t->ttyp == HOLE ||
  228. 			 t->ttyp == TRAPDOOR)) ) {
  229.  
  230. 	    if (Levitation) {
  231. 		You_feel("a tug from the iron ball.");
  232. 		if (t) t->tseen = 1;
  233. 	    } else {
  234. 		struct monst *victim;
  235.  
  236. 		You("are jerked back by the iron ball!");
  237. 		if ((victim = m_at(uchain->ox, uchain->oy)) != 0) {
  238. 		    int tmp;
  239.  
  240. 		    tmp = -2 + Luck + find_mac(victim);
  241. 		    tmp += omon_adj(victim, uball, TRUE);
  242. 		    if (tmp >= rnd(20))
  243. 			(void) hmon(victim,uball,1);
  244. 		    else
  245. 			miss(xname(uball), victim);
  246.  
  247. 		}		/* now check again in case mon died */
  248. 		if (!m_at(uchain->ox, uchain->oy)) {
  249. 		    u.ux = uchain->ox;
  250. 		    u.uy = uchain->oy;
  251. 		    newsym(u.ux0, u.uy0);
  252. 		}
  253. 		nomul(0);
  254.  
  255. 		*bc_control = BC_BALL;
  256. 		move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
  257. 		*ballx = uchain->ox;
  258. 		*bally = uchain->oy;
  259. 		move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy);
  260. 		spoteffects(TRUE);
  261. 		return FALSE;
  262. 	    }
  263. 	}
  264.  
  265. 	*bc_control = BC_BALL|BC_CHAIN;
  266.  
  267. 	move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
  268. 	if (dist2(x, y, u.ux, u.uy) > 2) {
  269. 	    /* Awful case: we're still in range of the ball, so we thought we
  270. 	     * could only move the chain, but it turned out that the target
  271. 	     * square for the chain was rock, so we had to drag it instead.
  272. 	     * But we can't drag it either, because we teleported and are more
  273. 	     * than one square from our old position.  Revert to the teleport
  274. 	     * behavior.
  275. 	     */
  276. 	    *ballx = *chainx = x;
  277. 	    *bally = *chainy = y;
  278. 	} else {
  279. 	    *ballx  = uchain->ox;
  280. 	    *bally  = uchain->oy;
  281. 	    *chainx = u.ux;
  282. 	    *chainy = u.uy;
  283. 	}
  284. 	*cause_delay = TRUE;
  285. 	return TRUE;
  286. }

drop_ball

  1. /*
  2. *  drop_ball()
  3. *
  4. *  The punished hero drops or throws her iron ball.  If the hero is
  5. *  blind, we must reset the order and glyph.  Check for side effects.
  6. *  This routine expects the ball to be already placed.
  7. *
  8. *  Should not be called while swallowed.
  9. */
  10. void
  11. drop_ball(x, y)
  12. xchar x, y;
  13. {
  14. if (Blind) {
  15. 	u.bc_order = bc_order();			/* get the order */
  16. 							/* pick up glyph */
  17. 	u.bglyph = (u.bc_order) ? u.cglyph : levl[x][y].glyph;
  18. }
  19.  
  20. if (x != u.ux || y != u.uy) {
  21. 	struct trap *t;
  22. 	const char *pullmsg = "The ball pulls you out of the %s!";
  23.  
  24. 	if (u.utrap && u.utraptype != TT_INFLOOR) {
  25. 	    switch(u.utraptype) {
  26. 	    case TT_PIT:
  27. 		pline(pullmsg, "pit");
  28. 		break;
  29. 	    case TT_WEB:
  30. 		pline(pullmsg, "web");
  31. 		pline_The("web is destroyed!");
  32. 		deltrap(t_at(u.ux,u.uy));
  33. 		break;
  34. 	    case TT_LAVA:
  35. 		pline(pullmsg, "lava");
  36. 		break;
  37. 	    case TT_BEARTRAP: {
  38. 		register long side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
  39. 		pline(pullmsg, "bear trap");
  40. 		set_wounded_legs(side, rn1(1000, 500));
  41. #ifdef STEED
  42. 		if (!u.usteed)
  43. #endif
  44. 		{
  45. 		    Your("%s %s is severely damaged.",
  46. 					(side == LEFT_SIDE) ? "left" : "right",
  47. 					body_part(LEG));
  48. 		    losehp(2, "leg damage from being pulled out of a bear trap",
  49. 					KILLED_BY);
  50. 		}
  51. 		break;
  52. 	      }
  53. 	    }
  54. 	    u.utrap = 0;
  55. 	    fill_pit(u.ux, u.uy);
  56. 	}
  57.  
  58. 	u.ux0 = u.ux;
  59. 	u.uy0 = u.uy;
  60. 	if (!Levitation && !MON_AT(x, y) && !u.utrap &&
  61. 			    (is_pool(x, y) ||
  62. 			     ((t = t_at(x, y)) &&
  63. 			      (t->ttyp == PIT || t->ttyp == SPIKED_PIT ||
  64. 			       t->ttyp == TRAPDOOR || t->ttyp == HOLE)))) {
  65. 	    u.ux = x;
  66. 	    u.uy = y;
  67. 	} else {
  68. 	    u.ux = x - u.dx;
  69. 	    u.uy = y - u.dy;
  70. 	}
  71. 	vision_full_recalc = 1;	/* hero has moved, recalculate vision later */
  72.  
  73. 	if (Blind) {
  74. 	    /* drop glyph under the chain */
  75. 	    if (u.bc_felt & BC_CHAIN)
  76. 		levl[uchain->ox][uchain->oy].glyph = u.cglyph;
  77. 	    u.bc_felt  = 0;		/* feel nothing */
  78. 	    /* pick up new glyph */
  79. 	    u.cglyph = (u.bc_order) ? u.bglyph : levl[u.ux][u.uy].glyph;
  80. 	}
  81. 	movobj(uchain,u.ux,u.uy);	/* has a newsym */
  82. 	if (Blind) {
  83. 	    u.bc_order = bc_order();
  84. 	}
  85. 	newsym(u.ux0,u.uy0);		/* clean up old position */
  86. 	if (u.ux0 != u.ux || u.uy0 != u.uy) {
  87. 	    spoteffects(TRUE);
  88. 	    if (In_sokoban(&u.uz))
  89. 		change_luck(-1);	/* Sokoban guilt */
  90. 	}
  91. }
  92. }

litter

  1. STATIC_OVL void
  2. litter()
  3. {
  4. 	struct obj *otmp = invent, *nextobj;
  5. 	int capacity = weight_cap();
  6.  
  7. 	while (otmp) {
  8. 		nextobj = otmp->nobj;
  9. 		if ((otmp != uball) && (rnd(capacity) <= (int)otmp->owt)) {
  10. 			if (canletgo(otmp, "")) {
  11. 				Your("%s you down the stairs.",
  12. 				     aobjnam(otmp, "follow"));
  13. 				dropx(otmp);
  14. 			}
  15. 		}
  16. 		otmp = nextobj;
  17. 	}
  18. }

Called only by drag_down in ball.c. If the iron ball drags you downstairs, you have a chance to drop items, based on the weight of each item compared to your total carrying capacity.

drag_down

  1. void
  2. drag_down()
  3. {
  4. 	boolean forward;
  5. 	uchar dragchance = 3;
  6.  
  7. 	/*
  8. 	 *	Assume that the ball falls forward if:
  9. 	 *
  10. 	 *	a) the character is wielding it, or
  11. 	 *	b) the character has both hands available to hold it (i.e. is
  12. 	 *	   not wielding any weapon), or
  13. 	 *	c) (perhaps) it falls forward out of his non-weapon hand
  14. 	 */
  15.  
  16. 	forward = carried(uball) && (uwep == uball || !uwep || !rn2(3));
  17.  
  18. 	if (carried(uball))
  19. 		You("lose your grip on the iron ball.");
  20.  
  21. 	if (forward) {
  22. 		if(rn2(6)) {
  23. 			pline_The("iron ball drags you downstairs!");
  24. 			losehp(rnd(6), "dragged downstairs by an iron ball",
  25. 				NO_KILLER_PREFIX);
  26. 			litter();
  27. 		}
  28. 	} else {
  29. 		if(rn2(2)) {
  30. 			pline_The("iron ball smacks into you!");
  31. 			losehp(rnd(20), "iron ball collision", KILLED_BY_AN);
  32. 			exercise(A_STR, FALSE);
  33. 			dragchance -= 2;
  34. 		}
  35. 		if( (int) dragchance >= rnd(6)) {
  36. 			pline_The("iron ball drags you downstairs!");
  37. 			losehp(rnd(3), "dragged downstairs by an iron ball",
  38. 				NO_KILLER_PREFIX);
  39. 			exercise(A_STR, FALSE);
  40. 			litter();
  41. 		}
  42. 	}
  43. }
  44.  
  45. /*ball.c*/