Home | History | Annotate | Line # | Download | only in huntd
shots.c revision 1.3
      1 /*	$NetBSD: shots.c,v 1.3 1997/10/11 08:13:50 lukem Exp $	*/
      2 /*
      3  *  Hunt
      4  *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
      5  *  San Francisco, California
      6  */
      7 
      8 #include <sys/cdefs.h>
      9 #ifndef lint
     10 __RCSID("$NetBSD: shots.c,v 1.3 1997/10/11 08:13:50 lukem Exp $");
     11 #endif /* not lint */
     12 
     13 # include	<err.h>
     14 # include	<signal.h>
     15 # include	<stdlib.h>
     16 # include	"hunt.h"
     17 
     18 # define	PLUS_DELTA(x, max)	if (x < max) x++; else x--
     19 # define	MINUS_DELTA(x, min)	if (x > min) x--; else x++
     20 
     21 static	void	chkshot __P((BULLET *, BULLET *));
     22 static	void	chkslime __P((BULLET *, BULLET *));
     23 static	void	explshot __P((BULLET *, int, int));
     24 static	void	find_under __P((BULLET *, BULLET *));
     25 static	int	iswall __P((int, int));
     26 static	void	mark_boot __P((BULLET *));
     27 static	void	mark_player __P((BULLET *));
     28 #ifdef DRONE
     29 static	void	move_drone __P((BULLET *));
     30 #endif
     31 static	void	move_flyer __P((PLAYER *));
     32 static	int	move_normal_shot __P((BULLET *));
     33 static	void	move_slime __P((BULLET *, int, BULLET *));
     34 static	void	save_bullet __P((BULLET *));
     35 static	void	zapshot __P((BULLET *, BULLET *));
     36 
     37 /*
     38  * moveshots:
     39  *	Move the shots already in the air, taking explosions into account
     40  */
     41 void
     42 moveshots()
     43 {
     44 	BULLET	*bp, *next;
     45 	PLAYER	*pp;
     46 	int	x, y;
     47 	BULLET	*blist;
     48 
     49 	rollexpl();
     50 	if (Bullets == NULL)
     51 		goto ret;
     52 
     53 	/*
     54 	 * First we move through the bullet list BULSPD times, looking
     55 	 * for things we may have run into.  If we do run into
     56 	 * something, we set up the explosion and disappear, checking
     57 	 * for damage to any player who got in the way.
     58 	 */
     59 
     60 	blist = Bullets;
     61 	Bullets = NULL;
     62 	for (bp = blist; bp != NULL; bp = next) {
     63 		next = bp->b_next;
     64 		x = bp->b_x;
     65 		y = bp->b_y;
     66 		Maze[y][x] = bp->b_over;
     67 		for (pp = Player; pp < End_player; pp++)
     68 			check(pp, y, x);
     69 # ifdef MONITOR
     70 		for (pp = Monitor; pp < End_monitor; pp++)
     71 			check(pp, y, x);
     72 # endif
     73 
     74 		switch (bp->b_type) {
     75 		  case SHOT:
     76 		  case GRENADE:
     77 		  case SATCHEL:
     78 		  case BOMB:
     79 			if (move_normal_shot(bp)) {
     80 				bp->b_next = Bullets;
     81 				Bullets = bp;
     82 			}
     83 			break;
     84 # ifdef OOZE
     85 		  case SLIME:
     86 			if (bp->b_expl || move_normal_shot(bp)) {
     87 				bp->b_next = Bullets;
     88 				Bullets = bp;
     89 			}
     90 			break;
     91 # endif
     92 # ifdef DRONE
     93 		  case DSHOT:
     94 			if (move_drone(bp)) {
     95 				bp->b_next = Bullets;
     96 				Bullets = bp;
     97 			}
     98 			break;
     99 # endif
    100 		  default:
    101 			bp->b_next = Bullets;
    102 			Bullets = bp;
    103 			break;
    104 		}
    105 	}
    106 
    107 	blist = Bullets;
    108 	Bullets = NULL;
    109 	for (bp = blist; bp != NULL; bp = next) {
    110 		next = bp->b_next;
    111 		if (!bp->b_expl) {
    112 			save_bullet(bp);
    113 # ifdef MONITOR
    114 			for (pp = Monitor; pp < End_monitor; pp++)
    115 				check(pp, bp->b_y, bp->b_x);
    116 # endif
    117 # ifdef DRONE
    118 			if (bp->b_type == DSHOT)
    119 				for (pp = Player; pp < End_player; pp++)
    120 					if (pp->p_scan >= 0)
    121 						check(pp, bp->b_y, bp->b_x);
    122 # endif
    123 			continue;
    124 		}
    125 
    126 		chkshot(bp, next);
    127 		free((char *) bp);
    128 	}
    129 
    130 	for (pp = Player; pp < End_player; pp++)
    131 		Maze[pp->p_y][pp->p_x] = pp->p_face;
    132 
    133 ret:
    134 # ifdef BOOTS
    135 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
    136 		if (pp->p_flying >= 0)
    137 			move_flyer(pp);
    138 # endif
    139 	for (pp = Player; pp < End_player; pp++) {
    140 # ifdef FLY
    141 		if (pp->p_flying >= 0)
    142 			move_flyer(pp);
    143 # endif
    144 		sendcom(pp, REFRESH);	/* Flush out the explosions */
    145 		look(pp);
    146 		sendcom(pp, REFRESH);
    147 	}
    148 # ifdef MONITOR
    149 	for (pp = Monitor; pp < End_monitor; pp++)
    150 		sendcom(pp, REFRESH);
    151 # endif
    152 
    153 	return;
    154 }
    155 
    156 /*
    157  * move_normal_shot:
    158  *	Move a normal shot along its trajectory
    159  */
    160 static int
    161 move_normal_shot(bp)
    162 	BULLET	*bp;
    163 {
    164 	int	i, x, y;
    165 	PLAYER	*pp;
    166 
    167 	for (i = 0; i < BULSPD; i++) {
    168 		if (bp->b_expl)
    169 			break;
    170 
    171 		x = bp->b_x;
    172 		y = bp->b_y;
    173 
    174 		switch (bp->b_face) {
    175 		  case LEFTS:
    176 			x--;
    177 			break;
    178 		  case RIGHT:
    179 			x++;
    180 			break;
    181 		  case ABOVE:
    182 			y--;
    183 			break;
    184 		  case BELOW:
    185 			y++;
    186 			break;
    187 		}
    188 
    189 		switch (Maze[y][x]) {
    190 		  case SHOT:
    191 			if (rand_num(100) < 5) {
    192 				zapshot(Bullets, bp);
    193 				zapshot(bp->b_next, bp);
    194 			}
    195 			break;
    196 		  case GRENADE:
    197 			if (rand_num(100) < 10) {
    198 				zapshot(Bullets, bp);
    199 				zapshot(bp->b_next, bp);
    200 			}
    201 			break;
    202 # ifdef	REFLECT
    203 		  case WALL4:	/* reflecting walls */
    204 			switch (bp->b_face) {
    205 			  case LEFTS:
    206 				bp->b_face = BELOW;
    207 				break;
    208 			  case RIGHT:
    209 				bp->b_face = ABOVE;
    210 				break;
    211 			  case ABOVE:
    212 				bp->b_face = RIGHT;
    213 				break;
    214 			  case BELOW:
    215 				bp->b_face = LEFTS;
    216 				break;
    217 			}
    218 			Maze[y][x] = WALL5;
    219 # ifdef MONITOR
    220 			for (pp = Monitor; pp < End_monitor; pp++)
    221 				check(pp, y, x);
    222 # endif
    223 			break;
    224 		  case WALL5:
    225 			switch (bp->b_face) {
    226 			  case LEFTS:
    227 				bp->b_face = ABOVE;
    228 				break;
    229 			  case RIGHT:
    230 				bp->b_face = BELOW;
    231 				break;
    232 			  case ABOVE:
    233 				bp->b_face = LEFTS;
    234 				break;
    235 			  case BELOW:
    236 				bp->b_face = RIGHT;
    237 				break;
    238 			}
    239 			Maze[y][x] = WALL4;
    240 # ifdef MONITOR
    241 			for (pp = Monitor; pp < End_monitor; pp++)
    242 				check(pp, y, x);
    243 # endif
    244 			break;
    245 # endif
    246 # ifdef RANDOM
    247 		  case DOOR:
    248 			switch (rand_num(4)) {
    249 			  case 0:
    250 				bp->b_face = ABOVE;
    251 				break;
    252 			  case 1:
    253 				bp->b_face = BELOW;
    254 				break;
    255 			  case 2:
    256 				bp->b_face = LEFTS;
    257 				break;
    258 			  case 3:
    259 				bp->b_face = RIGHT;
    260 				break;
    261 			}
    262 			break;
    263 # endif
    264 # ifdef FLY
    265 		  case FLYER:
    266 			pp = play_at(y, x);
    267 			message(pp, "Zing!");
    268 			break;
    269 # endif
    270 		  case LEFTS:
    271 		  case RIGHT:
    272 		  case BELOW:
    273 		  case ABOVE:
    274 			/*
    275 			 * give the person a chance to catch a
    276 			 * grenade if s/he is facing it
    277 			 */
    278 			pp = play_at(y, x);
    279 			pp->p_ident->i_shot += bp->b_charge;
    280 			if (opposite(bp->b_face, Maze[y][x])) {
    281 			    if (rand_num(100) < 10) {
    282 				if (bp->b_owner != NULL)
    283 					message(bp->b_owner,
    284 					    "Your charge was absorbed!");
    285 				if (bp->b_score != NULL)
    286 					bp->b_score->i_robbed += bp->b_charge;
    287 				pp->p_ammo += bp->b_charge;
    288 				if (pp->p_damage + bp->b_size * MINDAM
    289 				    > pp->p_damcap)
    290 					pp->p_ident->i_saved++;
    291 				message(pp, "Absorbed charge (good shield!)");
    292 				pp->p_ident->i_absorbed += bp->b_charge;
    293 				free((char *) bp);
    294 				(void) sprintf(Buf, "%3d", pp->p_ammo);
    295 				cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
    296 				outstr(pp, Buf, 3);
    297 				return FALSE;
    298 			    }
    299 			    pp->p_ident->i_faced += bp->b_charge;
    300 			}
    301 			/*
    302 			 * Small chance that the bullet just misses the
    303 			 * person.  If so, the bullet just goes on its
    304 			 * merry way without exploding.
    305 			 */
    306 			if (rand_num(100) < 5) {
    307 				pp->p_ident->i_ducked += bp->b_charge;
    308 				if (pp->p_damage + bp->b_size * MINDAM
    309 				    > pp->p_damcap)
    310 					pp->p_ident->i_saved++;
    311 				if (bp->b_score != NULL)
    312 					bp->b_score->i_missed += bp->b_charge;
    313 				message(pp, "Zing!");
    314 				if (bp->b_owner == NULL)
    315 					break;
    316 				message(bp->b_owner,
    317 					((bp->b_score->i_missed & 0x7) == 0x7) ?
    318 					"My!  What a bad shot you are!" :
    319 					"Missed him");
    320 				break;
    321 			}
    322 			/*
    323 			 * The shot hit that sucker!  Blow it up.
    324 			 */
    325 			/* FALLTHROUGH */
    326 # ifndef RANDOM
    327 		  case DOOR:
    328 # endif
    329 		  case WALL1:
    330 		  case WALL2:
    331 		  case WALL3:
    332 			bp->b_expl = TRUE;
    333 			break;
    334 		}
    335 
    336 		bp->b_x = x;
    337 		bp->b_y = y;
    338 	}
    339 	return TRUE;
    340 }
    341 
    342 # ifdef	DRONE
    343 /*
    344  * move_drone:
    345  *	Move the drone to the next square
    346  */
    347 static void
    348 move_drone(bp)
    349 	BULLET	*bp;
    350 {
    351 	int	mask, count;
    352 	int	n, dir;
    353 	PLAYER	*pp;
    354 
    355 	/*
    356 	 * See if we can give someone a blast
    357 	 */
    358 	if (isplayer(Maze[bp->b_y][bp->b_x - 1])) {
    359 		dir = WEST;
    360 		goto drone_move;
    361 	}
    362 	if (isplayer(Maze[bp->b_y - 1][bp->b_x])) {
    363 		dir = NORTH;
    364 		goto drone_move;
    365 	}
    366 	if (isplayer(Maze[bp->b_y + 1][bp->b_x])) {
    367 		dir = SOUTH;
    368 		goto drone_move;
    369 	}
    370 	if (isplayer(Maze[bp->b_y][bp->b_x + 1])) {
    371 		dir = EAST;
    372 		goto drone_move;
    373 	}
    374 
    375 	/*
    376 	 * Find out what directions are clear
    377 	 */
    378 	mask = count = 0;
    379 	if (!iswall(bp->b_y, bp->b_x - 1))
    380 		mask |= WEST, count++;
    381 	if (!iswall(bp->b_y - 1, bp->b_x))
    382 		mask |= NORTH, count++;
    383 	if (!iswall(bp->b_y + 1, bp->b_x))
    384 		mask |= SOUTH, count++;
    385 	if (!iswall(bp->b_y, bp->b_x + 1))
    386 		mask |= EAST, count++;
    387 
    388 	/*
    389 	 * All blocked up, just you wait
    390 	 */
    391 	if (count == 0)
    392 		return TRUE;
    393 
    394 	/*
    395 	 * Only one way to go.
    396 	 */
    397 	if (count == 1) {
    398 		dir = mask;
    399 		goto drone_move;
    400 	}
    401 
    402 	/*
    403 	 * Get rid of the direction that we came from
    404 	 */
    405 	switch (bp->b_face) {
    406 	  case LEFTS:
    407 		if (mask & EAST)
    408 			mask &= ~EAST, count--;
    409 		break;
    410 	  case RIGHT:
    411 		if (mask & WEST)
    412 			mask &= ~WEST, count--;
    413 		break;
    414 	  case ABOVE:
    415 		if (mask & SOUTH)
    416 			mask &= ~SOUTH, count--;
    417 		break;
    418 	  case BELOW:
    419 		if (mask & NORTH)
    420 			mask &= ~NORTH, count--;
    421 		break;
    422 	}
    423 
    424 	/*
    425 	 * Pick one of the remaining directions
    426 	 */
    427 	n = rand_num(count);
    428 	if (n >= 0 && mask & NORTH)
    429 		dir = NORTH, n--;
    430 	if (n >= 0 && mask & SOUTH)
    431 		dir = SOUTH, n--;
    432 	if (n >= 0 && mask & EAST)
    433 		dir = EAST, n--;
    434 	if (n >= 0 && mask & WEST)
    435 		dir = WEST, n--;
    436 
    437 	/*
    438 	 * Now that we know the direction of movement,
    439 	 * just update the position of the drone
    440 	 */
    441 drone_move:
    442 	switch (dir) {
    443 	  case WEST:
    444 		bp->b_x--;
    445 		bp->b_face = LEFTS;
    446 		break;
    447 	  case EAST:
    448 		bp->b_x++;
    449 		bp->b_face = RIGHT;
    450 		break;
    451 	  case NORTH:
    452 		bp->b_y--;
    453 		bp->b_face = ABOVE;
    454 		break;
    455 	  case SOUTH:
    456 		bp->b_y++;
    457 		bp->b_face = BELOW;
    458 		break;
    459 	}
    460 	switch (Maze[bp->b_y][bp->b_x]) {
    461 	  case LEFTS:
    462 	  case RIGHT:
    463 	  case BELOW:
    464 	  case ABOVE:
    465 		/*
    466 		 * give the person a chance to catch a
    467 		 * drone if s/he is facing it
    468 		 */
    469 		if (rand_num(100) < 1 &&
    470 		opposite(bp->b_face, Maze[bp->b_y][bp->b_x])) {
    471 			pp = play_at(bp->b_y, bp->b_x);
    472 			pp->p_ammo += bp->b_charge;
    473 			message(pp, "**** Absorbed drone ****");
    474 			free((char *) bp);
    475 			(void) sprintf(Buf, "%3d", pp->p_ammo);
    476 			cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
    477 			outstr(pp, Buf, 3);
    478 			return FALSE;
    479 		}
    480 		bp->b_expl = TRUE;
    481 		break;
    482 	}
    483 	return TRUE;
    484 }
    485 # endif
    486 
    487 /*
    488  * save_bullet:
    489  *	Put this bullet back onto the bullet list
    490  */
    491 static void
    492 save_bullet(bp)
    493 	BULLET	*bp;
    494 {
    495 	bp->b_over = Maze[bp->b_y][bp->b_x];
    496 	switch (bp->b_over) {
    497 	  case SHOT:
    498 	  case GRENADE:
    499 	  case SATCHEL:
    500 	  case BOMB:
    501 # ifdef OOZE
    502 	  case SLIME:
    503 # ifdef VOLCANO
    504 	  case LAVA:
    505 # endif
    506 # endif
    507 # ifdef DRONE
    508 	  case DSHOT:
    509 # endif
    510 		find_under(Bullets, bp);
    511 		break;
    512 	}
    513 
    514 	switch (bp->b_over) {
    515 	  case LEFTS:
    516 	  case RIGHT:
    517 	  case ABOVE:
    518 	  case BELOW:
    519 # ifdef FLY
    520 	  case FLYER:
    521 # endif
    522 		mark_player(bp);
    523 		break;
    524 # ifdef BOOTS
    525 	  case BOOT:
    526 	  case BOOT_PAIR:
    527 		mark_boot(bp);
    528 # endif
    529 
    530 	  default:
    531 		Maze[bp->b_y][bp->b_x] = bp->b_type;
    532 		break;
    533 	}
    534 
    535 	bp->b_next = Bullets;
    536 	Bullets = bp;
    537 }
    538 
    539 /*
    540  * move_flyer:
    541  *	Update the position of a player in flight
    542  */
    543 static void
    544 move_flyer(pp)
    545 	PLAYER	*pp;
    546 {
    547 	int	x, y;
    548 
    549 	if (pp->p_undershot) {
    550 		fixshots(pp->p_y, pp->p_x, pp->p_over);
    551 		pp->p_undershot = FALSE;
    552 	}
    553 	Maze[pp->p_y][pp->p_x] = pp->p_over;
    554 	x = pp->p_x + pp->p_flyx;
    555 	y = pp->p_y + pp->p_flyy;
    556 	if (x < 1) {
    557 		x = 1 - x;
    558 		pp->p_flyx = -pp->p_flyx;
    559 	}
    560 	else if (x > WIDTH - 2) {
    561 		x = (WIDTH - 2) - (x - (WIDTH - 2));
    562 		pp->p_flyx = -pp->p_flyx;
    563 	}
    564 	if (y < 1) {
    565 		y = 1 - y;
    566 		pp->p_flyy = -pp->p_flyy;
    567 	}
    568 	else if (y > HEIGHT - 2) {
    569 		y = (HEIGHT - 2) - (y - (HEIGHT - 2));
    570 		pp->p_flyy = -pp->p_flyy;
    571 	}
    572 again:
    573 	switch (Maze[y][x]) {
    574 	  default:
    575 		switch (rand_num(4)) {
    576 		  case 0:
    577 			PLUS_DELTA(x, WIDTH - 2);
    578 			break;
    579 		  case 1:
    580 			MINUS_DELTA(x, 1);
    581 			break;
    582 		  case 2:
    583 			PLUS_DELTA(y, HEIGHT - 2);
    584 			break;
    585 		  case 3:
    586 			MINUS_DELTA(y, 1);
    587 			break;
    588 		}
    589 		goto again;
    590 	  case WALL1:
    591 	  case WALL2:
    592 	  case WALL3:
    593 # ifdef	REFLECT
    594 	  case WALL4:
    595 	  case WALL5:
    596 # endif
    597 # ifdef	RANDOM
    598 	  case DOOR:
    599 # endif
    600 		if (pp->p_flying == 0)
    601 			pp->p_flying++;
    602 		break;
    603 	  case SPACE:
    604 		break;
    605 	}
    606 	pp->p_y = y;
    607 	pp->p_x = x;
    608 	if (pp->p_flying-- == 0) {
    609 # ifdef BOOTS
    610 		if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
    611 # endif
    612 			checkdam(pp, (PLAYER *) NULL, (IDENT *) NULL,
    613 				rand_num(pp->p_damage / 5), FALL);
    614 			pp->p_face = rand_dir();
    615 			showstat(pp);
    616 # ifdef BOOTS
    617 		}
    618 		else {
    619 			if (Maze[y][x] == BOOT)
    620 				pp->p_face = BOOT_PAIR;
    621 			Maze[y][x] = SPACE;
    622 		}
    623 # endif
    624 	}
    625 	pp->p_over = Maze[y][x];
    626 	Maze[y][x] = pp->p_face;
    627 	showexpl(y, x, pp->p_face);
    628 }
    629 
    630 /*
    631  * chkshot
    632  *	Handle explosions
    633  */
    634 static void
    635 chkshot(bp, next)
    636 	BULLET	*bp;
    637 	BULLET	*next;
    638 {
    639 	int	y, x;
    640 	int	dy, dx, absdy;
    641 	int	delta, damage;
    642 	char	expl;
    643 	PLAYER	*pp;
    644 
    645 	delta = 0;
    646 	switch (bp->b_type) {
    647 	  case SHOT:
    648 	  case MINE:
    649 	  case GRENADE:
    650 	  case GMINE:
    651 	  case SATCHEL:
    652 	  case BOMB:
    653 		delta = bp->b_size - 1;
    654 		break;
    655 # ifdef	OOZE
    656 	  case SLIME:
    657 # ifdef VOLCANO
    658 	  case LAVA:
    659 # endif
    660 		chkslime(bp, next);
    661 		return;
    662 # endif
    663 # ifdef DRONE
    664 	  case DSHOT:
    665 		bp->b_type = SLIME;
    666 		chkslime(bp, next);
    667 		return;
    668 # endif
    669 	}
    670 	for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
    671 		if (y < 0 || y >= HEIGHT)
    672 			continue;
    673 		dy = y - bp->b_y;
    674 		absdy = (dy < 0) ? -dy : dy;
    675 		for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
    676 			if (x < 0 || x >= WIDTH)
    677 				continue;
    678 			dx = x - bp->b_x;
    679 			if (dx == 0)
    680 				expl = (dy == 0) ? '*' : '|';
    681 			else if (dy == 0)
    682 				expl = '-';
    683 			else if (dx == dy)
    684 				expl = '\\';
    685 			else if (dx == -dy)
    686 				expl = '/';
    687 			else
    688 				expl = '*';
    689 			showexpl(y, x, expl);
    690 			switch (Maze[y][x]) {
    691 			  case LEFTS:
    692 			  case RIGHT:
    693 			  case ABOVE:
    694 			  case BELOW:
    695 # ifdef FLY
    696 			  case FLYER:
    697 # endif
    698 				if (dx < 0)
    699 					dx = -dx;
    700 				if (absdy > dx)
    701 					damage = bp->b_size - absdy;
    702 				else
    703 					damage = bp->b_size - dx;
    704 				pp = play_at(y, x);
    705 				checkdam(pp, bp->b_owner, bp->b_score,
    706 					damage * MINDAM, bp->b_type);
    707 				break;
    708 			  case GMINE:
    709 			  case MINE:
    710 				add_shot((Maze[y][x] == GMINE) ?
    711 					GRENADE : SHOT,
    712 					y, x, LEFTS,
    713 					(Maze[y][x] == GMINE) ?
    714 					GRENREQ : BULREQ,
    715 					(PLAYER *) NULL, TRUE, SPACE);
    716 				Maze[y][x] = SPACE;
    717 				break;
    718 			}
    719 		}
    720 	}
    721 }
    722 
    723 # ifdef	OOZE
    724 /*
    725  * chkslime:
    726  *	handle slime shot exploding
    727  */
    728 static void
    729 chkslime(bp, next)
    730 	BULLET	*bp;
    731 	BULLET	*next;
    732 {
    733 	BULLET	*nbp;
    734 
    735 	switch (Maze[bp->b_y][bp->b_x]) {
    736 	  case WALL1:
    737 	  case WALL2:
    738 	  case WALL3:
    739 # ifdef	REFLECT
    740 	  case WALL4:
    741 	  case WALL5:
    742 # endif
    743 # ifdef	RANDOM
    744 	  case DOOR:
    745 # endif
    746 		switch (bp->b_face) {
    747 		  case LEFTS:
    748 			bp->b_x++;
    749 			break;
    750 		  case RIGHT:
    751 			bp->b_x--;
    752 			break;
    753 		  case ABOVE:
    754 			bp->b_y++;
    755 			break;
    756 		  case BELOW:
    757 			bp->b_y--;
    758 			break;
    759 		}
    760 		break;
    761 	}
    762 	nbp = (BULLET *) malloc(sizeof (BULLET));
    763 	*nbp = *bp;
    764 # ifdef VOLCANO
    765 	move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next);
    766 # else
    767 	move_slime(nbp, SLIMESPEED, next);
    768 # endif
    769 }
    770 
    771 /*
    772  * move_slime:
    773  *	move the given slime shot speed times and add it back if
    774  *	it hasn't fizzled yet
    775  */
    776 void
    777 move_slime(bp, speed, next)
    778 	BULLET	*bp;
    779 	int	speed;
    780 	BULLET	*next;
    781 {
    782 	int	i, j, dirmask, count;
    783 	PLAYER	*pp;
    784 	BULLET	*nbp;
    785 
    786 	if (speed == 0) {
    787 		if (bp->b_charge <= 0)
    788 			free((char *) bp);
    789 		else
    790 			save_bullet(bp);
    791 		return;
    792 	}
    793 
    794 # ifdef VOLCANO
    795 	showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
    796 # else
    797 	showexpl(bp->b_y, bp->b_x, '*');
    798 # endif
    799 	switch (Maze[bp->b_y][bp->b_x]) {
    800 	  case LEFTS:
    801 	  case RIGHT:
    802 	  case ABOVE:
    803 	  case BELOW:
    804 # ifdef FLY
    805 	  case FLYER:
    806 # endif
    807 		pp = play_at(bp->b_y, bp->b_x);
    808 		message(pp, "You've been slimed.");
    809 		checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type);
    810 		break;
    811 	  case SHOT:
    812 	  case GRENADE:
    813 	  case SATCHEL:
    814 	  case BOMB:
    815 # ifdef DRONE
    816 	  case DSHOT:
    817 # endif
    818 		explshot(next, bp->b_y, bp->b_x);
    819 		explshot(Bullets, bp->b_y, bp->b_x);
    820 		break;
    821 	}
    822 
    823 	if (--bp->b_charge <= 0) {
    824 		free((char *) bp);
    825 		return;
    826 	}
    827 
    828 	dirmask = 0;
    829 	count = 0;
    830 	switch (bp->b_face) {
    831 	  case LEFTS:
    832 		if (!iswall(bp->b_y, bp->b_x - 1))
    833 			dirmask |= WEST, count++;
    834 		if (!iswall(bp->b_y - 1, bp->b_x))
    835 			dirmask |= NORTH, count++;
    836 		if (!iswall(bp->b_y + 1, bp->b_x))
    837 			dirmask |= SOUTH, count++;
    838 		if (dirmask == 0)
    839 			if (!iswall(bp->b_y, bp->b_x + 1))
    840 				dirmask |= EAST, count++;
    841 		break;
    842 	  case RIGHT:
    843 		if (!iswall(bp->b_y, bp->b_x + 1))
    844 			dirmask |= EAST, count++;
    845 		if (!iswall(bp->b_y - 1, bp->b_x))
    846 			dirmask |= NORTH, count++;
    847 		if (!iswall(bp->b_y + 1, bp->b_x))
    848 			dirmask |= SOUTH, count++;
    849 		if (dirmask == 0)
    850 			if (!iswall(bp->b_y, bp->b_x - 1))
    851 				dirmask |= WEST, count++;
    852 		break;
    853 	  case ABOVE:
    854 		if (!iswall(bp->b_y - 1, bp->b_x))
    855 			dirmask |= NORTH, count++;
    856 		if (!iswall(bp->b_y, bp->b_x - 1))
    857 			dirmask |= WEST, count++;
    858 		if (!iswall(bp->b_y, bp->b_x + 1))
    859 			dirmask |= EAST, count++;
    860 		if (dirmask == 0)
    861 			if (!iswall(bp->b_y + 1, bp->b_x))
    862 				dirmask |= SOUTH, count++;
    863 		break;
    864 	  case BELOW:
    865 		if (!iswall(bp->b_y + 1, bp->b_x))
    866 			dirmask |= SOUTH, count++;
    867 		if (!iswall(bp->b_y, bp->b_x - 1))
    868 			dirmask |= WEST, count++;
    869 		if (!iswall(bp->b_y, bp->b_x + 1))
    870 			dirmask |= EAST, count++;
    871 		if (dirmask == 0)
    872 			if (!iswall(bp->b_y - 1, bp->b_x))
    873 				dirmask |= NORTH, count++;
    874 		break;
    875 	}
    876 	if (count == 0) {
    877 		/*
    878 		 * No place to go.  Just sit here for a while and wait
    879 		 * for adjacent squares to clear out.
    880 		 */
    881 		save_bullet(bp);
    882 		return;
    883 	}
    884 	if (bp->b_charge < count) {
    885 		/* Only bp->b_charge paths may be taken */
    886 		while (count > bp->b_charge) {
    887 			if (dirmask & WEST)
    888 				dirmask &= ~WEST;
    889 			else if (dirmask & EAST)
    890 				dirmask &= ~EAST;
    891 			else if (dirmask & NORTH)
    892 				dirmask &= ~NORTH;
    893 			else if (dirmask & SOUTH)
    894 				dirmask &= ~SOUTH;
    895 			count--;
    896 		}
    897 	}
    898 
    899 	i = bp->b_charge / count;
    900 	j = bp->b_charge % count;
    901 	if (dirmask & WEST) {
    902 		count--;
    903 		nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
    904 			i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE);
    905 		move_slime(nbp, speed - 1, next);
    906 	}
    907 	if (dirmask & EAST) {
    908 		count--;
    909 		nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
    910 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
    911 			bp->b_score, TRUE, SPACE);
    912 		move_slime(nbp, speed - 1, next);
    913 	}
    914 	if (dirmask & NORTH) {
    915 		count--;
    916 		nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
    917 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
    918 			bp->b_score, TRUE, SPACE);
    919 		move_slime(nbp, speed - 1, next);
    920 	}
    921 	if (dirmask & SOUTH) {
    922 		count--;
    923 		nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
    924 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
    925 			bp->b_score, TRUE, SPACE);
    926 		move_slime(nbp, speed - 1, next);
    927 	}
    928 
    929 	free((char *) bp);
    930 }
    931 
    932 /*
    933  * iswall:
    934  *	returns whether the given location is a wall
    935  */
    936 static int
    937 iswall(y, x)
    938 	int	y, x;
    939 {
    940 	if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
    941 		return TRUE;
    942 	switch (Maze[y][x]) {
    943 	  case WALL1:
    944 	  case WALL2:
    945 	  case WALL3:
    946 # ifdef	REFLECT
    947 	  case WALL4:
    948 	  case WALL5:
    949 # endif
    950 # ifdef	RANDOM
    951 	  case DOOR:
    952 # endif
    953 # ifdef OOZE
    954 	  case SLIME:
    955 # ifdef VOLCANO
    956 	  case LAVA:
    957 # endif
    958 # endif
    959 		return TRUE;
    960 	}
    961 	return FALSE;
    962 }
    963 # endif
    964 
    965 /*
    966  * zapshot:
    967  *	Take a shot out of the air.
    968  */
    969 static void
    970 zapshot(blist, obp)
    971 	BULLET	*blist, *obp;
    972 {
    973 	BULLET	*bp;
    974 	FLAG	explode;
    975 
    976 	explode = FALSE;
    977 	for (bp = blist; bp != NULL; bp = bp->b_next) {
    978 		if (bp->b_x != obp->b_x || bp->b_y != obp->b_y)
    979 			continue;
    980 		if (bp->b_face == obp->b_face)
    981 			continue;
    982 		explode = TRUE;
    983 		break;
    984 	}
    985 	if (!explode)
    986 		return;
    987 	explshot(blist, obp->b_y, obp->b_x);
    988 }
    989 
    990 /*
    991  * explshot -
    992  *	Make all shots at this location blow up
    993  */
    994 void
    995 explshot(blist, y, x)
    996 	BULLET	*blist;
    997 	int	y, x;
    998 {
    999 	BULLET	*bp;
   1000 
   1001 	for (bp = blist; bp != NULL; bp = bp->b_next)
   1002 		if (bp->b_x == x && bp->b_y == y) {
   1003 			bp->b_expl = TRUE;
   1004 			if (bp->b_owner != NULL)
   1005 				message(bp->b_owner, "Shot intercepted");
   1006 		}
   1007 }
   1008 
   1009 /*
   1010  * play_at:
   1011  *	Return a pointer to the player at the given location
   1012  */
   1013 PLAYER *
   1014 play_at(y, x)
   1015 	int	y, x;
   1016 {
   1017 	PLAYER	*pp;
   1018 
   1019 	for (pp = Player; pp < End_player; pp++)
   1020 		if (pp->p_x == x && pp->p_y == y)
   1021 			return pp;
   1022 	errx(1, "driver: couldn't find player at (%d,%d)", x, y);
   1023 	/* NOTREACHED */
   1024 }
   1025 
   1026 /*
   1027  * opposite:
   1028  *	Return TRUE if the bullet direction faces the opposite direction
   1029  *	of the player in the maze
   1030  */
   1031 int
   1032 opposite(face, dir)
   1033 	int	face;
   1034 	char	dir;
   1035 {
   1036 	switch (face) {
   1037 	  case LEFTS:
   1038 		return (dir == RIGHT);
   1039 	  case RIGHT:
   1040 		return (dir == LEFTS);
   1041 	  case ABOVE:
   1042 		return (dir == BELOW);
   1043 	  case BELOW:
   1044 		return (dir == ABOVE);
   1045 	  default:
   1046 		return FALSE;
   1047 	}
   1048 }
   1049 
   1050 /*
   1051  * is_bullet:
   1052  *	Is there a bullet at the given coordinates?  If so, return
   1053  *	a pointer to the bullet, otherwise return NULL
   1054  */
   1055 BULLET *
   1056 is_bullet(y, x)
   1057 	int	y, x;
   1058 {
   1059 	BULLET	*bp;
   1060 
   1061 	for (bp = Bullets; bp != NULL; bp = bp->b_next)
   1062 		if (bp->b_y == y && bp->b_x == x)
   1063 			return bp;
   1064 	return NULL;
   1065 }
   1066 
   1067 /*
   1068  * fixshots:
   1069  *	change the underlying character of the shots at a location
   1070  *	to the given character.
   1071  */
   1072 void
   1073 fixshots(y, x, over)
   1074 	int	y, x;
   1075 	char	over;
   1076 {
   1077 	BULLET	*bp;
   1078 
   1079 	for (bp = Bullets; bp != NULL; bp = bp->b_next)
   1080 		if (bp->b_y == y && bp->b_x == x)
   1081 			bp->b_over = over;
   1082 }
   1083 
   1084 /*
   1085  * find_under:
   1086  *	find the underlying character for a bullet when it lands
   1087  *	on another bullet.
   1088  */
   1089 static void
   1090 find_under(blist, bp)
   1091 	BULLET	*blist, *bp;
   1092 {
   1093 	BULLET	*nbp;
   1094 
   1095 	for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
   1096 		if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
   1097 			bp->b_over = nbp->b_over;
   1098 			break;
   1099 		}
   1100 }
   1101 
   1102 /*
   1103  * mark_player:
   1104  *	mark a player as under a shot
   1105  */
   1106 static void
   1107 mark_player(bp)
   1108 	BULLET	*bp;
   1109 {
   1110 	PLAYER	*pp;
   1111 
   1112 	for (pp = Player; pp < End_player; pp++)
   1113 		if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
   1114 			pp->p_undershot = TRUE;
   1115 			break;
   1116 		}
   1117 }
   1118 
   1119 # ifdef BOOTS
   1120 /*
   1121  * mark_boot:
   1122  *	mark a boot as under a shot
   1123  */
   1124 static void
   1125 mark_boot(bp)
   1126 	BULLET	*bp;
   1127 {
   1128 	PLAYER	*pp;
   1129 
   1130 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
   1131 		if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
   1132 			pp->p_undershot = TRUE;
   1133 			break;
   1134 		}
   1135 }
   1136 # endif
   1137