shots.c revision 1.1.1.2 1 /*
2 * Copyright (c) 1983-2003, Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * + Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * + Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * + Neither the name of the University of California, San Francisco nor
15 * the names of its contributors may be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 # include "hunt.h"
33 # include <signal.h>
34
35 # define PLUS_DELTA(x, max) if (x < max) x++; else x--
36 # define MINUS_DELTA(x, min) if (x > min) x--; else x++
37
38 /*
39 * moveshots:
40 * Move the shots already in the air, taking explosions into account
41 */
42 moveshots()
43 {
44 register BULLET *bp, *next;
45 register PLAYER *pp;
46 register int x, y;
47 register BULLET *blist;
48
49 rollexpl();
50 if (Bullets == NULL)
51 goto ret;
52
53 /*
54 * First we move through the bullet list BULSPD times, looking
55 * for things we may have run into. If we do run into
56 * something, we set up the explosion and disappear, checking
57 * for damage to any player who got in the way.
58 */
59
60 blist = Bullets;
61 Bullets = NULL;
62 for (bp = blist; bp != NULL; bp = next) {
63 next = bp->b_next;
64 x = bp->b_x;
65 y = bp->b_y;
66 Maze[y][x] = bp->b_over;
67 for (pp = Player; pp < End_player; pp++)
68 check(pp, y, x);
69 # ifdef MONITOR
70 for (pp = Monitor; pp < End_monitor; pp++)
71 check(pp, y, x);
72 # endif
73
74 switch (bp->b_type) {
75 case SHOT:
76 case GRENADE:
77 case SATCHEL:
78 case BOMB:
79 if (move_normal_shot(bp)) {
80 bp->b_next = Bullets;
81 Bullets = bp;
82 }
83 break;
84 # ifdef OOZE
85 case SLIME:
86 if (bp->b_expl || move_normal_shot(bp)) {
87 bp->b_next = Bullets;
88 Bullets = bp;
89 }
90 break;
91 # endif
92 # ifdef DRONE
93 case DSHOT:
94 if (move_drone(bp)) {
95 bp->b_next = Bullets;
96 Bullets = bp;
97 }
98 break;
99 # endif
100 default:
101 bp->b_next = Bullets;
102 Bullets = bp;
103 break;
104 }
105 }
106
107 blist = Bullets;
108 Bullets = NULL;
109 for (bp = blist; bp != NULL; bp = next) {
110 next = bp->b_next;
111 if (!bp->b_expl) {
112 save_bullet(bp);
113 # ifdef MONITOR
114 for (pp = Monitor; pp < End_monitor; pp++)
115 check(pp, bp->b_y, bp->b_x);
116 # endif
117 # ifdef DRONE
118 if (bp->b_type == DSHOT)
119 for (pp = Player; pp < End_player; pp++)
120 if (pp->p_scan >= 0)
121 check(pp, bp->b_y, bp->b_x);
122 # endif
123 continue;
124 }
125
126 chkshot(bp, next);
127 free((char *) bp);
128 }
129
130 for (pp = Player; pp < End_player; pp++)
131 Maze[pp->p_y][pp->p_x] = pp->p_face;
132
133 ret:
134 # ifdef BOOTS
135 for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
136 if (pp->p_flying >= 0)
137 move_flyer(pp);
138 # endif
139 for (pp = Player; pp < End_player; pp++) {
140 # ifdef FLY
141 if (pp->p_flying >= 0)
142 move_flyer(pp);
143 # endif
144 sendcom(pp, REFRESH); /* Flush out the explosions */
145 look(pp);
146 sendcom(pp, REFRESH);
147 }
148 # ifdef MONITOR
149 for (pp = Monitor; pp < End_monitor; pp++)
150 sendcom(pp, REFRESH);
151 # endif
152
153 return;
154 }
155
156 /*
157 * move_normal_shot:
158 * Move a normal shot along its trajectory
159 */
160 move_normal_shot(bp)
161 register BULLET *bp;
162 {
163 register int i, x, y;
164 register 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 move_drone(bp)
347 register BULLET *bp;
348 {
349 register int mask, count;
350 register int n, dir;
351 register PLAYER *pp;
352
353 /*
354 * See if we can give someone a blast
355 */
356 if (isplayer(Maze[bp->b_y][bp->b_x - 1])) {
357 dir = WEST;
358 goto drone_move;
359 }
360 if (isplayer(Maze[bp->b_y - 1][bp->b_x])) {
361 dir = NORTH;
362 goto drone_move;
363 }
364 if (isplayer(Maze[bp->b_y + 1][bp->b_x])) {
365 dir = SOUTH;
366 goto drone_move;
367 }
368 if (isplayer(Maze[bp->b_y][bp->b_x + 1])) {
369 dir = EAST;
370 goto drone_move;
371 }
372
373 /*
374 * Find out what directions are clear
375 */
376 mask = count = 0;
377 if (!iswall(bp->b_y, bp->b_x - 1))
378 mask |= WEST, count++;
379 if (!iswall(bp->b_y - 1, bp->b_x))
380 mask |= NORTH, count++;
381 if (!iswall(bp->b_y + 1, bp->b_x))
382 mask |= SOUTH, count++;
383 if (!iswall(bp->b_y, bp->b_x + 1))
384 mask |= EAST, count++;
385
386 /*
387 * All blocked up, just you wait
388 */
389 if (count == 0)
390 return TRUE;
391
392 /*
393 * Only one way to go.
394 */
395 if (count == 1) {
396 dir = mask;
397 goto drone_move;
398 }
399
400 /*
401 * Get rid of the direction that we came from
402 */
403 switch (bp->b_face) {
404 case LEFTS:
405 if (mask & EAST)
406 mask &= ~EAST, count--;
407 break;
408 case RIGHT:
409 if (mask & WEST)
410 mask &= ~WEST, count--;
411 break;
412 case ABOVE:
413 if (mask & SOUTH)
414 mask &= ~SOUTH, count--;
415 break;
416 case BELOW:
417 if (mask & NORTH)
418 mask &= ~NORTH, count--;
419 break;
420 }
421
422 /*
423 * Pick one of the remaining directions
424 */
425 n = rand_num(count);
426 if (n >= 0 && mask & NORTH)
427 dir = NORTH, n--;
428 if (n >= 0 && mask & SOUTH)
429 dir = SOUTH, n--;
430 if (n >= 0 && mask & EAST)
431 dir = EAST, n--;
432 if (n >= 0 && mask & WEST)
433 dir = WEST, n--;
434
435 /*
436 * Now that we know the direction of movement,
437 * just update the position of the drone
438 */
439 drone_move:
440 switch (dir) {
441 case WEST:
442 bp->b_x--;
443 bp->b_face = LEFTS;
444 break;
445 case EAST:
446 bp->b_x++;
447 bp->b_face = RIGHT;
448 break;
449 case NORTH:
450 bp->b_y--;
451 bp->b_face = ABOVE;
452 break;
453 case SOUTH:
454 bp->b_y++;
455 bp->b_face = BELOW;
456 break;
457 }
458 switch (Maze[bp->b_y][bp->b_x]) {
459 case LEFTS:
460 case RIGHT:
461 case BELOW:
462 case ABOVE:
463 /*
464 * give the person a chance to catch a
465 * drone if s/he is facing it
466 */
467 if (rand_num(100) < 1 &&
468 opposite(bp->b_face, Maze[bp->b_y][bp->b_x])) {
469 pp = play_at(bp->b_y, bp->b_x);
470 pp->p_ammo += bp->b_charge;
471 message(pp, "**** Absorbed drone ****");
472 free((char *) bp);
473 (void) sprintf(Buf, "%3d", pp->p_ammo);
474 cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
475 outstr(pp, Buf, 3);
476 return FALSE;
477 }
478 bp->b_expl = TRUE;
479 break;
480 }
481 return TRUE;
482 }
483 # endif
484
485 /*
486 * save_bullet:
487 * Put this bullet back onto the bullet list
488 */
489 save_bullet(bp)
490 register BULLET *bp;
491 {
492 bp->b_over = Maze[bp->b_y][bp->b_x];
493 switch (bp->b_over) {
494 case SHOT:
495 case GRENADE:
496 case SATCHEL:
497 case BOMB:
498 # ifdef OOZE
499 case SLIME:
500 # ifdef VOLCANO
501 case LAVA:
502 # endif
503 # endif
504 # ifdef DRONE
505 case DSHOT:
506 # endif
507 find_under(Bullets, bp);
508 break;
509 }
510
511 switch (bp->b_over) {
512 case LEFTS:
513 case RIGHT:
514 case ABOVE:
515 case BELOW:
516 # ifdef FLY
517 case FLYER:
518 # endif
519 mark_player(bp);
520 break;
521 # ifdef BOOTS
522 case BOOT:
523 case BOOT_PAIR:
524 mark_boot(bp);
525 # endif
526
527 default:
528 Maze[bp->b_y][bp->b_x] = bp->b_type;
529 break;
530 }
531
532 bp->b_next = Bullets;
533 Bullets = bp;
534 }
535
536 /*
537 * move_flyer:
538 * Update the position of a player in flight
539 */
540 move_flyer(pp)
541 register PLAYER *pp;
542 {
543 register int x, y;
544
545 if (pp->p_undershot) {
546 fixshots(pp->p_y, pp->p_x, pp->p_over);
547 pp->p_undershot = FALSE;
548 }
549 Maze[pp->p_y][pp->p_x] = pp->p_over;
550 x = pp->p_x + pp->p_flyx;
551 y = pp->p_y + pp->p_flyy;
552 if (x < 1) {
553 x = 1 - x;
554 pp->p_flyx = -pp->p_flyx;
555 }
556 else if (x > WIDTH - 2) {
557 x = (WIDTH - 2) - (x - (WIDTH - 2));
558 pp->p_flyx = -pp->p_flyx;
559 }
560 if (y < 1) {
561 y = 1 - y;
562 pp->p_flyy = -pp->p_flyy;
563 }
564 else if (y > HEIGHT - 2) {
565 y = (HEIGHT - 2) - (y - (HEIGHT - 2));
566 pp->p_flyy = -pp->p_flyy;
567 }
568 again:
569 switch (Maze[y][x]) {
570 default:
571 switch (rand_num(4)) {
572 case 0:
573 PLUS_DELTA(x, WIDTH - 2);
574 break;
575 case 1:
576 MINUS_DELTA(x, 1);
577 break;
578 case 2:
579 PLUS_DELTA(y, HEIGHT - 2);
580 break;
581 case 3:
582 MINUS_DELTA(y, 1);
583 break;
584 }
585 goto again;
586 case WALL1:
587 case WALL2:
588 case WALL3:
589 # ifdef REFLECT
590 case WALL4:
591 case WALL5:
592 # endif
593 # ifdef RANDOM
594 case DOOR:
595 # endif
596 if (pp->p_flying == 0)
597 pp->p_flying++;
598 break;
599 case SPACE:
600 break;
601 }
602 pp->p_y = y;
603 pp->p_x = x;
604 if (pp->p_flying-- == 0) {
605 # ifdef BOOTS
606 if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
607 # endif
608 checkdam(pp, (PLAYER *) NULL, (IDENT *) NULL,
609 rand_num(pp->p_damage / 5), FALL);
610 pp->p_face = rand_dir();
611 showstat(pp);
612 # ifdef BOOTS
613 }
614 else {
615 if (Maze[y][x] == BOOT)
616 pp->p_face = BOOT_PAIR;
617 Maze[y][x] = SPACE;
618 }
619 # endif
620 }
621 pp->p_over = Maze[y][x];
622 Maze[y][x] = pp->p_face;
623 showexpl(y, x, pp->p_face);
624 }
625
626 /*
627 * chkshot
628 * Handle explosions
629 */
630 chkshot(bp, next)
631 register BULLET *bp;
632 BULLET *next;
633 {
634 register int y, x;
635 register int dy, dx, absdy;
636 register int delta, damage;
637 register char expl;
638 register PLAYER *pp;
639
640 switch (bp->b_type) {
641 case SHOT:
642 case MINE:
643 case GRENADE:
644 case GMINE:
645 case SATCHEL:
646 case BOMB:
647 delta = bp->b_size - 1;
648 break;
649 # ifdef OOZE
650 case SLIME:
651 # ifdef VOLCANO
652 case LAVA:
653 # endif
654 chkslime(bp, next);
655 return;
656 # endif
657 # ifdef DRONE
658 case DSHOT:
659 bp->b_type = SLIME;
660 chkslime(bp, next);
661 return;
662 # endif
663 }
664 for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
665 if (y < 0 || y >= HEIGHT)
666 continue;
667 dy = y - bp->b_y;
668 absdy = (dy < 0) ? -dy : dy;
669 for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
670 if (x < 0 || x >= WIDTH)
671 continue;
672 dx = x - bp->b_x;
673 if (dx == 0)
674 expl = (dy == 0) ? '*' : '|';
675 else if (dy == 0)
676 expl = '-';
677 else if (dx == dy)
678 expl = '\\';
679 else if (dx == -dy)
680 expl = '/';
681 else
682 expl = '*';
683 showexpl(y, x, expl);
684 switch (Maze[y][x]) {
685 case LEFTS:
686 case RIGHT:
687 case ABOVE:
688 case BELOW:
689 # ifdef FLY
690 case FLYER:
691 # endif
692 if (dx < 0)
693 dx = -dx;
694 if (absdy > dx)
695 damage = bp->b_size - absdy;
696 else
697 damage = bp->b_size - dx;
698 pp = play_at(y, x);
699 checkdam(pp, bp->b_owner, bp->b_score,
700 damage * MINDAM, bp->b_type);
701 break;
702 case GMINE:
703 case MINE:
704 add_shot((Maze[y][x] == GMINE) ?
705 GRENADE : SHOT,
706 y, x, LEFTS,
707 (Maze[y][x] == GMINE) ?
708 GRENREQ : BULREQ,
709 (PLAYER *) NULL, TRUE, SPACE);
710 Maze[y][x] = SPACE;
711 break;
712 }
713 }
714 }
715 }
716
717 # ifdef OOZE
718 /*
719 * chkslime:
720 * handle slime shot exploding
721 */
722 chkslime(bp, next)
723 register BULLET *bp;
724 BULLET *next;
725 {
726 register BULLET *nbp;
727
728 switch (Maze[bp->b_y][bp->b_x]) {
729 case WALL1:
730 case WALL2:
731 case WALL3:
732 # ifdef REFLECT
733 case WALL4:
734 case WALL5:
735 # endif
736 # ifdef RANDOM
737 case DOOR:
738 # endif
739 switch (bp->b_face) {
740 case LEFTS:
741 bp->b_x++;
742 break;
743 case RIGHT:
744 bp->b_x--;
745 break;
746 case ABOVE:
747 bp->b_y++;
748 break;
749 case BELOW:
750 bp->b_y--;
751 break;
752 }
753 break;
754 }
755 nbp = (BULLET *) malloc(sizeof (BULLET));
756 *nbp = *bp;
757 # ifdef VOLCANO
758 move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next);
759 # else
760 move_slime(nbp, SLIMESPEED, next);
761 # endif
762 }
763
764 /*
765 * move_slime:
766 * move the given slime shot speed times and add it back if
767 * it hasn't fizzled yet
768 */
769 move_slime(bp, speed, next)
770 register BULLET *bp;
771 register int speed;
772 BULLET *next;
773 {
774 register int i, j, dirmask, count;
775 register PLAYER *pp;
776 register BULLET *nbp;
777
778 if (speed == 0) {
779 if (bp->b_charge <= 0)
780 free((char *) bp);
781 else
782 save_bullet(bp);
783 return;
784 }
785
786 # ifdef VOLCANO
787 showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
788 # else
789 showexpl(bp->b_y, bp->b_x, '*');
790 # endif
791 switch (Maze[bp->b_y][bp->b_x]) {
792 case LEFTS:
793 case RIGHT:
794 case ABOVE:
795 case BELOW:
796 # ifdef FLY
797 case FLYER:
798 # endif
799 pp = play_at(bp->b_y, bp->b_x);
800 message(pp, "You've been slimed.");
801 checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type);
802 break;
803 case SHOT:
804 case GRENADE:
805 case SATCHEL:
806 case BOMB:
807 # ifdef DRONE
808 case DSHOT:
809 # endif
810 explshot(next, bp->b_y, bp->b_x);
811 explshot(Bullets, bp->b_y, bp->b_x);
812 break;
813 }
814
815 if (--bp->b_charge <= 0) {
816 free((char *) bp);
817 return;
818 }
819
820 dirmask = 0;
821 count = 0;
822 switch (bp->b_face) {
823 case LEFTS:
824 if (!iswall(bp->b_y, bp->b_x - 1))
825 dirmask |= WEST, count++;
826 if (!iswall(bp->b_y - 1, bp->b_x))
827 dirmask |= NORTH, count++;
828 if (!iswall(bp->b_y + 1, bp->b_x))
829 dirmask |= SOUTH, count++;
830 if (dirmask == 0)
831 if (!iswall(bp->b_y, bp->b_x + 1))
832 dirmask |= EAST, count++;
833 break;
834 case RIGHT:
835 if (!iswall(bp->b_y, bp->b_x + 1))
836 dirmask |= EAST, count++;
837 if (!iswall(bp->b_y - 1, bp->b_x))
838 dirmask |= NORTH, count++;
839 if (!iswall(bp->b_y + 1, bp->b_x))
840 dirmask |= SOUTH, count++;
841 if (dirmask == 0)
842 if (!iswall(bp->b_y, bp->b_x - 1))
843 dirmask |= WEST, count++;
844 break;
845 case ABOVE:
846 if (!iswall(bp->b_y - 1, bp->b_x))
847 dirmask |= NORTH, count++;
848 if (!iswall(bp->b_y, bp->b_x - 1))
849 dirmask |= WEST, count++;
850 if (!iswall(bp->b_y, bp->b_x + 1))
851 dirmask |= EAST, count++;
852 if (dirmask == 0)
853 if (!iswall(bp->b_y + 1, bp->b_x))
854 dirmask |= SOUTH, count++;
855 break;
856 case BELOW:
857 if (!iswall(bp->b_y + 1, bp->b_x))
858 dirmask |= SOUTH, count++;
859 if (!iswall(bp->b_y, bp->b_x - 1))
860 dirmask |= WEST, count++;
861 if (!iswall(bp->b_y, bp->b_x + 1))
862 dirmask |= EAST, count++;
863 if (dirmask == 0)
864 if (!iswall(bp->b_y - 1, bp->b_x))
865 dirmask |= NORTH, count++;
866 break;
867 }
868 if (count == 0) {
869 /*
870 * No place to go. Just sit here for a while and wait
871 * for adjacent squares to clear out.
872 */
873 save_bullet(bp);
874 return;
875 }
876 if (bp->b_charge < count) {
877 /* Only bp->b_charge paths may be taken */
878 while (count > bp->b_charge) {
879 if (dirmask & WEST)
880 dirmask &= ~WEST;
881 else if (dirmask & EAST)
882 dirmask &= ~EAST;
883 else if (dirmask & NORTH)
884 dirmask &= ~NORTH;
885 else if (dirmask & SOUTH)
886 dirmask &= ~SOUTH;
887 count--;
888 }
889 }
890
891 i = bp->b_charge / count;
892 j = bp->b_charge % count;
893 if (dirmask & WEST) {
894 count--;
895 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
896 i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE);
897 move_slime(nbp, speed - 1, next);
898 }
899 if (dirmask & EAST) {
900 count--;
901 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
902 (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
903 bp->b_score, TRUE, SPACE);
904 move_slime(nbp, speed - 1, next);
905 }
906 if (dirmask & NORTH) {
907 count--;
908 nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
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 & SOUTH) {
914 count--;
915 nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
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
921 free((char *) bp);
922 }
923
924 /*
925 * iswall:
926 * returns whether the given location is a wall
927 */
928 iswall(y, x)
929 register int y, x;
930 {
931 if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
932 return TRUE;
933 switch (Maze[y][x]) {
934 case WALL1:
935 case WALL2:
936 case WALL3:
937 # ifdef REFLECT
938 case WALL4:
939 case WALL5:
940 # endif
941 # ifdef RANDOM
942 case DOOR:
943 # endif
944 # ifdef OOZE
945 case SLIME:
946 # ifdef VOLCANO
947 case LAVA:
948 # endif
949 # endif
950 return TRUE;
951 }
952 return FALSE;
953 }
954 # endif
955
956 /*
957 * zapshot:
958 * Take a shot out of the air.
959 */
960 zapshot(blist, obp)
961 register BULLET *blist, *obp;
962 {
963 register BULLET *bp;
964 register FLAG explode;
965
966 explode = FALSE;
967 for (bp = blist; bp != NULL; bp = bp->b_next) {
968 if (bp->b_x != obp->b_x || bp->b_y != obp->b_y)
969 continue;
970 if (bp->b_face == obp->b_face)
971 continue;
972 explode = TRUE;
973 break;
974 }
975 if (!explode)
976 return;
977 explshot(blist, obp->b_y, obp->b_x);
978 }
979
980 /*
981 * explshot -
982 * Make all shots at this location blow up
983 */
984 explshot(blist, y, x)
985 register BULLET *blist;
986 register int y, x;
987 {
988 register BULLET *bp;
989
990 for (bp = blist; bp != NULL; bp = bp->b_next)
991 if (bp->b_x == x && bp->b_y == y) {
992 bp->b_expl = TRUE;
993 if (bp->b_owner != NULL)
994 message(bp->b_owner, "Shot intercepted");
995 }
996 }
997
998 /*
999 * play_at:
1000 * Return a pointer to the player at the given location
1001 */
1002 PLAYER *
1003 play_at(y, x)
1004 register int y, x;
1005 {
1006 register PLAYER *pp;
1007
1008 for (pp = Player; pp < End_player; pp++)
1009 if (pp->p_x == x && pp->p_y == y)
1010 return pp;
1011 fprintf(stderr, "driver: couldn't find player at (%d,%d)\n", x, y);
1012 abort();
1013 /* NOTREACHED */
1014 }
1015
1016 /*
1017 * opposite:
1018 * Return TRUE if the bullet direction faces the opposite direction
1019 * of the player in the maze
1020 */
1021 opposite(face, dir)
1022 int face;
1023 char dir;
1024 {
1025 switch (face) {
1026 case LEFTS:
1027 return (dir == RIGHT);
1028 case RIGHT:
1029 return (dir == LEFTS);
1030 case ABOVE:
1031 return (dir == BELOW);
1032 case BELOW:
1033 return (dir == ABOVE);
1034 default:
1035 return FALSE;
1036 }
1037 }
1038
1039 /*
1040 * is_bullet:
1041 * Is there a bullet at the given coordinates? If so, return
1042 * a pointer to the bullet, otherwise return NULL
1043 */
1044 BULLET *
1045 is_bullet(y, x)
1046 register int y, x;
1047 {
1048 register BULLET *bp;
1049
1050 for (bp = Bullets; bp != NULL; bp = bp->b_next)
1051 if (bp->b_y == y && bp->b_x == x)
1052 return bp;
1053 return NULL;
1054 }
1055
1056 /*
1057 * fixshots:
1058 * change the underlying character of the shots at a location
1059 * to the given character.
1060 */
1061 fixshots(y, x, over)
1062 register int y, x;
1063 char over;
1064 {
1065 register BULLET *bp;
1066
1067 for (bp = Bullets; bp != NULL; bp = bp->b_next)
1068 if (bp->b_y == y && bp->b_x == x)
1069 bp->b_over = over;
1070 }
1071
1072 /*
1073 * find_under:
1074 * find the underlying character for a bullet when it lands
1075 * on another bullet.
1076 */
1077 find_under(blist, bp)
1078 register BULLET *blist, *bp;
1079 {
1080 register BULLET *nbp;
1081
1082 for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
1083 if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
1084 bp->b_over = nbp->b_over;
1085 break;
1086 }
1087 }
1088
1089 /*
1090 * mark_player:
1091 * mark a player as under a shot
1092 */
1093 mark_player(bp)
1094 register BULLET *bp;
1095 {
1096 register PLAYER *pp;
1097
1098 for (pp = Player; pp < End_player; pp++)
1099 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
1100 pp->p_undershot = TRUE;
1101 break;
1102 }
1103 }
1104
1105 # ifdef BOOTS
1106 /*
1107 * mark_boot:
1108 * mark a boot as under a shot
1109 */
1110 mark_boot(bp)
1111 register BULLET *bp;
1112 {
1113 register PLAYER *pp;
1114
1115 for (pp = Boot; pp < &Boot[NBOOTS]; 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 # endif
1122