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