xref: /netbsd-src/games/hunt/huntd/shots.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: shots.c,v 1.14 2014/03/29 21:43:19 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.14 2014/03/29 21:43:19 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 static 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 static 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