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