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