Home | History | Annotate | Line # | Download | only in huntd
      1 /*	$NetBSD: shots.c,v 1.16 2021/05/02 12:50:45 rillig 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.16 2021/05/02 12:50:45 rillig 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 #ifndef RANDOM
    351 			/* FALLTHROUGH */
    352 		  case DOOR:
    353 #endif
    354 			/* FALLTHROUGH */
    355 		  case WALL1:
    356 		  case WALL2:
    357 		  case WALL3:
    358 			bp->b_expl = true;
    359 			break;
    360 		}
    361 
    362 		bp->b_x = x;
    363 		bp->b_y = y;
    364 	}
    365 	return true;
    366 }
    367 
    368 #ifdef DRONE
    369 /*
    370  * move_drone:
    371  *	Move the drone to the next square
    372  */
    373 static void
    374 move_drone(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(bp);
    500 			(void) snprintf(Buf, sizeof(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(BULLET *bp)
    518 {
    519 	bp->b_over = Maze[bp->b_y][bp->b_x];
    520 	switch (bp->b_over) {
    521 	  case SHOT:
    522 	  case GRENADE:
    523 	  case SATCHEL:
    524 	  case BOMB:
    525 #ifdef OOZE
    526 	  case SLIME:
    527 #ifdef VOLCANO
    528 	  case LAVA:
    529 #endif
    530 #endif
    531 #ifdef DRONE
    532 	  case DSHOT:
    533 #endif
    534 		find_under(Bullets, bp);
    535 		break;
    536 	}
    537 
    538 	switch (bp->b_over) {
    539 	  case LEFTS:
    540 	  case RIGHT:
    541 	  case ABOVE:
    542 	  case BELOW:
    543 #ifdef FLY
    544 	  case FLYER:
    545 #endif
    546 		mark_player(bp);
    547 		break;
    548 #ifdef BOOTS
    549 	  case BOOT:
    550 	  case BOOT_PAIR:
    551 		mark_boot(bp);
    552 		break;
    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(PLAYER *pp)
    570 {
    571 	int x, y;
    572 
    573 	if (pp->p_undershot) {
    574 		fixshots(pp->p_y, pp->p_x, pp->p_over);
    575 		pp->p_undershot = false;
    576 	}
    577 	Maze[pp->p_y][pp->p_x] = pp->p_over;
    578 	x = pp->p_x + pp->p_flyx;
    579 	y = pp->p_y + pp->p_flyy;
    580 	if (x < 1) {
    581 		x = 1 - x;
    582 		pp->p_flyx = -pp->p_flyx;
    583 	}
    584 	else if (x > WIDTH - 2) {
    585 		x = (WIDTH - 2) - (x - (WIDTH - 2));
    586 		pp->p_flyx = -pp->p_flyx;
    587 	}
    588 	if (y < 1) {
    589 		y = 1 - y;
    590 		pp->p_flyy = -pp->p_flyy;
    591 	}
    592 	else if (y > HEIGHT - 2) {
    593 		y = (HEIGHT - 2) - (y - (HEIGHT - 2));
    594 		pp->p_flyy = -pp->p_flyy;
    595 	}
    596 again:
    597 	switch (Maze[y][x]) {
    598 	  default:
    599 		switch (rand_num(4)) {
    600 		  case 0:
    601 			PLUS_DELTA(x, WIDTH - 2);
    602 			break;
    603 		  case 1:
    604 			MINUS_DELTA(x, 1);
    605 			break;
    606 		  case 2:
    607 			PLUS_DELTA(y, HEIGHT - 2);
    608 			break;
    609 		  case 3:
    610 			MINUS_DELTA(y, 1);
    611 			break;
    612 		}
    613 		goto again;
    614 	  case WALL1:
    615 	  case WALL2:
    616 	  case WALL3:
    617 #ifdef REFLECT
    618 	  case WALL4:
    619 	  case WALL5:
    620 #endif
    621 #ifdef RANDOM
    622 	  case DOOR:
    623 #endif
    624 		if (pp->p_flying == 0)
    625 			pp->p_flying++;
    626 		break;
    627 	  case SPACE:
    628 		break;
    629 	}
    630 	pp->p_y = y;
    631 	pp->p_x = x;
    632 	if (pp->p_flying-- == 0) {
    633 #ifdef BOOTS
    634 		if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
    635 #endif
    636 			checkdam(pp, NULL, NULL,
    637 				rand_num(pp->p_damage / 5), FALL);
    638 			pp->p_face = rand_dir();
    639 			showstat(pp);
    640 #ifdef BOOTS
    641 		}
    642 		else {
    643 			if (Maze[y][x] == BOOT)
    644 				pp->p_face = BOOT_PAIR;
    645 			Maze[y][x] = SPACE;
    646 		}
    647 #endif
    648 	}
    649 	pp->p_over = Maze[y][x];
    650 	Maze[y][x] = pp->p_face;
    651 	showexpl(y, x, pp->p_face);
    652 }
    653 
    654 /*
    655  * chkshot
    656  *	Handle explosions
    657  */
    658 static void
    659 chkshot(BULLET *bp, BULLET *next)
    660 {
    661 	int y, x;
    662 	int dy, dx, absdy;
    663 	int delta, damage;
    664 	char expl;
    665 	PLAYER *pp;
    666 
    667 	delta = 0;
    668 	switch (bp->b_type) {
    669 	  case SHOT:
    670 	  case MINE:
    671 	  case GRENADE:
    672 	  case GMINE:
    673 	  case SATCHEL:
    674 	  case BOMB:
    675 		delta = bp->b_size - 1;
    676 		break;
    677 #ifdef OOZE
    678 	  case SLIME:
    679 #ifdef VOLCANO
    680 	  case LAVA:
    681 #endif
    682 		chkslime(bp, next);
    683 		return;
    684 #endif
    685 #ifdef DRONE
    686 	  case DSHOT:
    687 		bp->b_type = SLIME;
    688 		chkslime(bp, next);
    689 		return;
    690 #endif
    691 	}
    692 	for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
    693 		if (y < 0 || y >= HEIGHT)
    694 			continue;
    695 		dy = y - bp->b_y;
    696 		absdy = (dy < 0) ? -dy : dy;
    697 		for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
    698 			if (x < 0 || x >= WIDTH)
    699 				continue;
    700 			dx = x - bp->b_x;
    701 			if (dx == 0)
    702 				expl = (dy == 0) ? '*' : '|';
    703 			else if (dy == 0)
    704 				expl = '-';
    705 			else if (dx == dy)
    706 				expl = '\\';
    707 			else if (dx == -dy)
    708 				expl = '/';
    709 			else
    710 				expl = '*';
    711 			showexpl(y, x, expl);
    712 			switch (Maze[y][x]) {
    713 			  case LEFTS:
    714 			  case RIGHT:
    715 			  case ABOVE:
    716 			  case BELOW:
    717 #ifdef FLY
    718 			  case FLYER:
    719 #endif
    720 				if (dx < 0)
    721 					dx = -dx;
    722 				if (absdy > dx)
    723 					damage = bp->b_size - absdy;
    724 				else
    725 					damage = bp->b_size - dx;
    726 				pp = play_at(y, x);
    727 				checkdam(pp, bp->b_owner, bp->b_score,
    728 					damage * MINDAM, bp->b_type);
    729 				break;
    730 			  case GMINE:
    731 			  case MINE:
    732 				add_shot((Maze[y][x] == GMINE) ?
    733 					GRENADE : SHOT,
    734 					y, x, LEFTS,
    735 					(Maze[y][x] == GMINE) ?
    736 					GRENREQ : BULREQ,
    737 					NULL, true, SPACE);
    738 				Maze[y][x] = SPACE;
    739 				break;
    740 			}
    741 		}
    742 	}
    743 }
    744 
    745 #ifdef OOZE
    746 /*
    747  * chkslime:
    748  *	handle slime shot exploding
    749  */
    750 static void
    751 chkslime(BULLET *bp, BULLET *next)
    752 {
    753 	BULLET	*nbp;
    754 
    755 	switch (Maze[bp->b_y][bp->b_x]) {
    756 	  case WALL1:
    757 	  case WALL2:
    758 	  case WALL3:
    759 #ifdef REFLECT
    760 	  case WALL4:
    761 	  case WALL5:
    762 #endif
    763 #ifdef RANDOM
    764 	  case DOOR:
    765 #endif
    766 		switch (bp->b_face) {
    767 		  case LEFTS:
    768 			bp->b_x++;
    769 			break;
    770 		  case RIGHT:
    771 			bp->b_x--;
    772 			break;
    773 		  case ABOVE:
    774 			bp->b_y++;
    775 			break;
    776 		  case BELOW:
    777 			bp->b_y--;
    778 			break;
    779 		}
    780 		break;
    781 	}
    782 	nbp = malloc(sizeof(*nbp));
    783 	*nbp = *bp;
    784 #ifdef VOLCANO
    785 	move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next);
    786 #else
    787 	move_slime(nbp, SLIMESPEED, next);
    788 #endif
    789 }
    790 
    791 /*
    792  * move_slime:
    793  *	move the given slime shot speed times and add it back if
    794  *	it hasn't fizzled yet
    795  */
    796 static void
    797 move_slime(BULLET *bp, int speed, BULLET *next)
    798 {
    799 	int i, j, dirmask, count;
    800 	PLAYER *pp;
    801 	BULLET *nbp;
    802 
    803 	if (speed == 0) {
    804 		if (bp->b_charge <= 0)
    805 			free(bp);
    806 		else
    807 			save_bullet(bp);
    808 		return;
    809 	}
    810 
    811 #ifdef VOLCANO
    812 	showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
    813 #else
    814 	showexpl(bp->b_y, bp->b_x, '*');
    815 #endif
    816 	switch (Maze[bp->b_y][bp->b_x]) {
    817 	  case LEFTS:
    818 	  case RIGHT:
    819 	  case ABOVE:
    820 	  case BELOW:
    821 #ifdef FLY
    822 	  case FLYER:
    823 #endif
    824 		pp = play_at(bp->b_y, bp->b_x);
    825 		message(pp, "You've been slimed.");
    826 		checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type);
    827 		break;
    828 	  case SHOT:
    829 	  case GRENADE:
    830 	  case SATCHEL:
    831 	  case BOMB:
    832 #ifdef DRONE
    833 	  case DSHOT:
    834 #endif
    835 		explshot(next, bp->b_y, bp->b_x);
    836 		explshot(Bullets, bp->b_y, bp->b_x);
    837 		break;
    838 	}
    839 
    840 	if (--bp->b_charge <= 0) {
    841 		free(bp);
    842 		return;
    843 	}
    844 
    845 	dirmask = 0;
    846 	count = 0;
    847 	switch (bp->b_face) {
    848 	  case LEFTS:
    849 		if (!iswall(bp->b_y, bp->b_x - 1))
    850 			dirmask |= WEST, count++;
    851 		if (!iswall(bp->b_y - 1, bp->b_x))
    852 			dirmask |= NORTH, count++;
    853 		if (!iswall(bp->b_y + 1, bp->b_x))
    854 			dirmask |= SOUTH, count++;
    855 		if (dirmask == 0)
    856 			if (!iswall(bp->b_y, bp->b_x + 1))
    857 				dirmask |= EAST, count++;
    858 		break;
    859 	  case RIGHT:
    860 		if (!iswall(bp->b_y, bp->b_x + 1))
    861 			dirmask |= EAST, count++;
    862 		if (!iswall(bp->b_y - 1, bp->b_x))
    863 			dirmask |= NORTH, count++;
    864 		if (!iswall(bp->b_y + 1, bp->b_x))
    865 			dirmask |= SOUTH, count++;
    866 		if (dirmask == 0)
    867 			if (!iswall(bp->b_y, bp->b_x - 1))
    868 				dirmask |= WEST, count++;
    869 		break;
    870 	  case ABOVE:
    871 		if (!iswall(bp->b_y - 1, bp->b_x))
    872 			dirmask |= NORTH, count++;
    873 		if (!iswall(bp->b_y, bp->b_x - 1))
    874 			dirmask |= WEST, count++;
    875 		if (!iswall(bp->b_y, bp->b_x + 1))
    876 			dirmask |= EAST, count++;
    877 		if (dirmask == 0)
    878 			if (!iswall(bp->b_y + 1, bp->b_x))
    879 				dirmask |= SOUTH, count++;
    880 		break;
    881 	  case BELOW:
    882 		if (!iswall(bp->b_y + 1, bp->b_x))
    883 			dirmask |= SOUTH, count++;
    884 		if (!iswall(bp->b_y, bp->b_x - 1))
    885 			dirmask |= WEST, count++;
    886 		if (!iswall(bp->b_y, bp->b_x + 1))
    887 			dirmask |= EAST, count++;
    888 		if (dirmask == 0)
    889 			if (!iswall(bp->b_y - 1, bp->b_x))
    890 				dirmask |= NORTH, count++;
    891 		break;
    892 	}
    893 	if (count == 0) {
    894 		/*
    895 		 * No place to go.  Just sit here for a while and wait
    896 		 * for adjacent squares to clear out.
    897 		 */
    898 		save_bullet(bp);
    899 		return;
    900 	}
    901 	if (bp->b_charge < count) {
    902 		/* Only bp->b_charge paths may be taken */
    903 		while (count > bp->b_charge) {
    904 			if (dirmask & WEST)
    905 				dirmask &= ~WEST;
    906 			else if (dirmask & EAST)
    907 				dirmask &= ~EAST;
    908 			else if (dirmask & NORTH)
    909 				dirmask &= ~NORTH;
    910 			else if (dirmask & SOUTH)
    911 				dirmask &= ~SOUTH;
    912 			count--;
    913 		}
    914 	}
    915 
    916 	i = bp->b_charge / count;
    917 	j = bp->b_charge % count;
    918 	if (dirmask & WEST) {
    919 		count--;
    920 		nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
    921 			i, bp->b_size, bp->b_owner, bp->b_score, true, SPACE);
    922 		move_slime(nbp, speed - 1, next);
    923 	}
    924 	if (dirmask & EAST) {
    925 		count--;
    926 		nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
    927 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
    928 			bp->b_score, true, SPACE);
    929 		move_slime(nbp, speed - 1, next);
    930 	}
    931 	if (dirmask & NORTH) {
    932 		count--;
    933 		nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
    934 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
    935 			bp->b_score, true, SPACE);
    936 		move_slime(nbp, speed - 1, next);
    937 	}
    938 	if (dirmask & SOUTH) {
    939 		count--;
    940 		nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
    941 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
    942 			bp->b_score, true, SPACE);
    943 		move_slime(nbp, speed - 1, next);
    944 	}
    945 
    946 	free(bp);
    947 }
    948 
    949 /*
    950  * iswall:
    951  *	returns whether the given location is a wall
    952  */
    953 static bool
    954 iswall(int y, int x)
    955 {
    956 	if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
    957 		return true;
    958 	switch (Maze[y][x]) {
    959 	  case WALL1:
    960 	  case WALL2:
    961 	  case WALL3:
    962 #ifdef REFLECT
    963 	  case WALL4:
    964 	  case WALL5:
    965 #endif
    966 #ifdef RANDOM
    967 	  case DOOR:
    968 #endif
    969 #ifdef OOZE
    970 	  case SLIME:
    971 #ifdef VOLCANO
    972 	  case LAVA:
    973 #endif
    974 #endif
    975 		return true;
    976 	}
    977 	return false;
    978 }
    979 #endif
    980 
    981 /*
    982  * zapshot:
    983  *	Take a shot out of the air.
    984  */
    985 static void
    986 zapshot(BULLET *blist, BULLET *obp)
    987 {
    988 	BULLET *bp;
    989 	bool explode;
    990 
    991 	explode = false;
    992 	for (bp = blist; bp != NULL; bp = bp->b_next) {
    993 		if (bp->b_x != obp->b_x || bp->b_y != obp->b_y)
    994 			continue;
    995 		if (bp->b_face == obp->b_face)
    996 			continue;
    997 		explode = true;
    998 		break;
    999 	}
   1000 	if (!explode)
   1001 		return;
   1002 	explshot(blist, obp->b_y, obp->b_x);
   1003 }
   1004 
   1005 /*
   1006  * explshot -
   1007  *	Make all shots at this location blow up
   1008  */
   1009 static void
   1010 explshot(BULLET *blist, int y, int x)
   1011 {
   1012 	BULLET *bp;
   1013 
   1014 	for (bp = blist; bp != NULL; bp = bp->b_next)
   1015 		if (bp->b_x == x && bp->b_y == y) {
   1016 			bp->b_expl = true;
   1017 			if (bp->b_owner != NULL)
   1018 				message(bp->b_owner, "Shot intercepted");
   1019 		}
   1020 }
   1021 
   1022 /*
   1023  * play_at:
   1024  *	Return a pointer to the player at the given location
   1025  */
   1026 PLAYER *
   1027 play_at(int y, int x)
   1028 {
   1029 	PLAYER *pp;
   1030 
   1031 	for (pp = Player; pp < End_player; pp++)
   1032 		if (pp->p_x == x && pp->p_y == y)
   1033 			return pp;
   1034 	errx(1, "driver: couldn't find player at (%d,%d)", x, y);
   1035 	/* NOTREACHED */
   1036 }
   1037 
   1038 /*
   1039  * opposite:
   1040  *	Return true if the bullet direction faces the opposite direction
   1041  *	of the player in the maze
   1042  */
   1043 bool
   1044 opposite(int face, char dir)
   1045 {
   1046 	switch (face) {
   1047 	  case LEFTS:
   1048 		return (dir == RIGHT);
   1049 	  case RIGHT:
   1050 		return (dir == LEFTS);
   1051 	  case ABOVE:
   1052 		return (dir == BELOW);
   1053 	  case BELOW:
   1054 		return (dir == ABOVE);
   1055 	  default:
   1056 		return false;
   1057 	}
   1058 }
   1059 
   1060 /*
   1061  * is_bullet:
   1062  *	Is there a bullet at the given coordinates?  If so, return
   1063  *	a pointer to the bullet, otherwise return NULL
   1064  */
   1065 BULLET *
   1066 is_bullet(int y, int x)
   1067 {
   1068 	BULLET *bp;
   1069 
   1070 	for (bp = Bullets; bp != NULL; bp = bp->b_next)
   1071 		if (bp->b_y == y && bp->b_x == x)
   1072 			return bp;
   1073 	return NULL;
   1074 }
   1075 
   1076 /*
   1077  * fixshots:
   1078  *	change the underlying character of the shots at a location
   1079  *	to the given character.
   1080  */
   1081 void
   1082 fixshots(int y, int x, char over)
   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 			bp->b_over = over;
   1089 }
   1090 
   1091 /*
   1092  * find_under:
   1093  *	find the underlying character for a bullet when it lands
   1094  *	on another bullet.
   1095  */
   1096 static void
   1097 find_under(BULLET *blist, BULLET *bp)
   1098 {
   1099 	BULLET *nbp;
   1100 
   1101 	for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
   1102 		if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
   1103 			bp->b_over = nbp->b_over;
   1104 			break;
   1105 		}
   1106 }
   1107 
   1108 /*
   1109  * mark_player:
   1110  *	mark a player as under a shot
   1111  */
   1112 static void
   1113 mark_player(BULLET *bp)
   1114 {
   1115 	PLAYER *pp;
   1116 
   1117 	for (pp = Player; pp < End_player; pp++)
   1118 		if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
   1119 			pp->p_undershot = true;
   1120 			break;
   1121 		}
   1122 }
   1123 
   1124 #ifdef BOOTS
   1125 /*
   1126  * mark_boot:
   1127  *	mark a boot as under a shot
   1128  */
   1129 static void
   1130 mark_boot(BULLET *bp)
   1131 {
   1132 	PLAYER *pp;
   1133 
   1134 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
   1135 		if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
   1136 			pp->p_undershot = true;
   1137 			break;
   1138 		}
   1139 }
   1140 #endif
   1141