xref: /netbsd-src/games/hunt/huntd/shots.c (revision 1182a44c59cae4d586117d55eca24b4b8b173211)
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
moveshots(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
move_normal_shot(BULLET * bp)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
move_drone(BULLET * bp)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
save_bullet(BULLET * bp)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
move_flyer(PLAYER * pp)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
chkshot(BULLET * bp,BULLET * next)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
chkslime(BULLET * bp,BULLET * next)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
move_slime(BULLET * bp,int speed,BULLET * next)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
iswall(int y,int x)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
zapshot(BULLET * blist,BULLET * obp)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
explshot(BULLET * blist,int y,int x)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 *
play_at(int y,int x)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
opposite(int face,char dir)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 *
is_bullet(int y,int x)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
fixshots(int y,int x,char over)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
find_under(BULLET * blist,BULLET * bp)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
mark_player(BULLET * bp)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
mark_boot(BULLET * bp)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