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