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