games: Massive style(9) cleanup commit. Reduces differences to NetBSD.
[dragonfly.git] / games / hunt / huntd / shots.c
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  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. 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  * 3. 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  * $OpenBSD: shots.c,v 1.9 2006/03/27 00:10:15 tedu Exp $
32  * $NetBSD: shots.c,v 1.3 1997/10/11 08:13:50 lukem Exp $
33  * $DragonFly: src/games/hunt/huntd/shots.c,v 1.2 2008/09/04 16:12:51 swildner Exp $
34  */
35
36 #include <err.h>
37 #include <signal.h>
38 #include <stdlib.h>
39 #include <syslog.h>
40 #include "hunt.h"
41 #include "conf.h"
42 #include "server.h"
43
44 #define PLUS_DELTA(x, max)      if (x < max) x++; else x--
45 #define MINUS_DELTA(x, min)     if (x > min) x--; else x++
46
47 static  void    chkshot(BULLET *, BULLET *);
48 static  void    chkslime(BULLET *, BULLET *);
49 static  void    explshot(BULLET *, int, int);
50 static  void    find_under(BULLET *, BULLET *);
51 static  int     iswall(int, int);
52 static  void    mark_boot(BULLET *);
53 static  void    mark_player(BULLET *);
54 static  int     move_drone(BULLET *);
55 static  void    move_flyer(PLAYER *);
56 static  int     move_normal_shot(BULLET *);
57 static  void    move_slime(BULLET *, int, BULLET *);
58 static  void    save_bullet(BULLET *);
59 static  void    zapshot(BULLET *, BULLET *);
60
61 /* Return true if there is pending activity */
62 int
63 can_moveshots(void)
64 {
65         PLAYER *pp;
66
67         /* Bullets are moving? */
68         if (Bullets)
69                 return 1;
70
71         /* Explosions are happening? */
72         if (can_rollexpl())
73                 return 1;
74
75         /* Things are flying? */
76         for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
77                 if (pp->p_flying >= 0)
78                         return 1;
79         for (pp = Player; pp < End_player; pp++)
80                 if (pp->p_flying >= 0)
81                         return 1;
82
83         /* Everything is quiet: */
84         return 0;
85 }
86
87 /*
88  * moveshots:
89  *      Move the shots already in the air, taking explosions into account
90  */
91 void
92 moveshots(void)
93 {
94         BULLET  *bp, *next;
95         PLAYER  *pp;
96         int     x, y;
97         BULLET  *blist;
98
99         rollexpl();
100         if (Bullets == NULL)
101                 goto no_bullets;
102
103         /*
104          * First we move through the bullet list conf_bulspd times, looking
105          * for things we may have run into.  If we do run into
106          * something, we set up the explosion and disappear, checking
107          * for damage to any player who got in the way.
108          */
109
110         /* Move the list to a working list */
111         blist = Bullets;
112         Bullets = NULL;
113
114         /* Work with bullets on the working list (blist) */
115         for (bp = blist; bp != NULL; bp = next) {
116                 next = bp->b_next;
117
118                 x = bp->b_x;
119                 y = bp->b_y;
120
121                 /* Un-draw the bullet on all screens: */
122                 Maze[y][x] = bp->b_over;
123                 check(ALL_PLAYERS, y, x);
124
125                 /* Decide how to move the bullet: */
126                 switch (bp->b_type) {
127
128                   /* Normal, atomic bullets: */
129                   case SHOT:
130                   case GRENADE:
131                   case SATCHEL:
132                   case BOMB:
133                         if (move_normal_shot(bp)) {
134                                 /* Still there: put back on the active list */
135                                 bp->b_next = Bullets;
136                                 Bullets = bp;
137                         }
138                         break;
139
140                   /* Slime bullets that explode into slime on impact: */
141                   case SLIME:
142                         if (bp->b_expl || move_normal_shot(bp)) {
143                                 /* Still there: put back on the active list */
144                                 bp->b_next = Bullets;
145                                 Bullets = bp;
146                         }
147                         break;
148
149                   /* Drones that wander about: */
150                   case DSHOT:
151                         if (move_drone(bp)) {
152                                 /* Still there: put back on the active list */
153                                 bp->b_next = Bullets;
154                                 Bullets = bp;
155                         }
156                         break;
157
158                   /* Other/unknown: */
159                   default:
160                         /* Place it back on the active list: */
161                         bp->b_next = Bullets;
162                         Bullets = bp;
163                         break;
164                 }
165         }
166
167         /* Again, hang the Bullets list off `blist' and work with that: */
168         blist = Bullets;
169         Bullets = NULL;
170         for (bp = blist; bp != NULL; bp = next) {
171                 next = bp->b_next;
172                 /* Is the bullet exploding? */
173                 if (!bp->b_expl) {
174                         /*
175                          * Its still flying through the air.
176                          * Put it back on the bullet list.
177                          */
178                         save_bullet(bp);
179
180                         /* All the monitors can see the bullet: */
181                         for (pp = Monitor; pp < End_monitor; pp++)
182                                 check(pp, bp->b_y, bp->b_x);
183
184                         /* All the scanning players can see the drone: */
185                         if (bp->b_type == DSHOT)
186                                 for (pp = Player; pp < End_player; pp++)
187                                         if (pp->p_scan >= 0)
188                                                 check(pp, bp->b_y, bp->b_x);
189                 } else {
190                         /* It is exploding. Check what we hit: */
191                         chkshot(bp, next);
192                         /* Release storage for the destroyed bullet: */
193                         free(bp);
194                 }
195         }
196
197         /* Re-draw all the players: (in case a bullet wiped them out) */
198         for (pp = Player; pp < End_player; pp++)
199                 Maze[pp->p_y][pp->p_x] = pp->p_face;
200
201 no_bullets:
202
203         /* Move flying boots through the air: */
204         for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
205                 if (pp->p_flying >= 0)
206                         move_flyer(pp);
207
208         /* Move flying players through the air: */
209         for (pp = Player; pp < End_player; pp++) {
210                 if (pp->p_flying >= 0)
211                         move_flyer(pp);
212                 /* Flush out the explosions: */
213                 sendcom(pp, REFRESH);
214                 look(pp);
215         }
216
217         /* Flush out and synchronise all the displays: */
218         sendcom(ALL_PLAYERS, REFRESH);
219 }
220
221 /*
222  * move_normal_shot:
223  *      Move a normal shot along its trajectory.
224  *      Returns false if the bullet no longer needs tracking.
225  */
226 static int
227 move_normal_shot(BULLET *bp)
228 {
229         int     i, x, y;
230         PLAYER  *pp;
231
232         /*
233          * Walk an unexploded bullet along conf_bulspd times, moving it
234          * one unit along each step. We flag it as exploding if it
235          * meets something.
236          */
237
238         for (i = 0; i < conf_bulspd; i++) {
239
240                 /* Stop if the bullet has already exploded: */
241                 if (bp->b_expl)
242                         break;
243
244                 /* Adjust the bullet's co-ordinates: */
245                 x = bp->b_x;
246                 y = bp->b_y;
247                 switch (bp->b_face) {
248                   case LEFTS:
249                         x--;
250                         break;
251                   case RIGHT:
252                         x++;
253                         break;
254                   case ABOVE:
255                         y--;
256                         break;
257                   case BELOW:
258                         y++;
259                         break;
260                 }
261
262
263                 /* Look at what the bullet is colliding with : */
264                 switch (Maze[y][x]) {
265                   /* Gun shots have a chance of collision: */
266                   case SHOT:
267                         if (rand_num(100) < conf_pshot_coll) {
268                                 zapshot(Bullets, bp);
269                                 zapshot(bp->b_next, bp);
270                         }
271                         break;
272                   /* Grenades only have a chance of collision: */
273                   case GRENADE:
274                         if (rand_num(100) < conf_pgren_coll) {
275                                 zapshot(Bullets, bp);
276                                 zapshot(bp->b_next, bp);
277                         }
278                         break;
279                   /* Reflecting walls richochet the bullet: */
280                   case WALL4:
281                         switch (bp->b_face) {
282                           case LEFTS:
283                                 bp->b_face = BELOW;
284                                 break;
285                           case RIGHT:
286                                 bp->b_face = ABOVE;
287                                 break;
288                           case ABOVE:
289                                 bp->b_face = RIGHT;
290                                 break;
291                           case BELOW:
292                                 bp->b_face = LEFTS;
293                                 break;
294                         }
295                         Maze[y][x] = WALL5;
296                         for (pp = Monitor; pp < End_monitor; pp++)
297                                 check(pp, y, x);
298                         break;
299                   case WALL5:
300                         switch (bp->b_face) {
301                           case LEFTS:
302                                 bp->b_face = ABOVE;
303                                 break;
304                           case RIGHT:
305                                 bp->b_face = BELOW;
306                                 break;
307                           case ABOVE:
308                                 bp->b_face = LEFTS;
309                                 break;
310                           case BELOW:
311                                 bp->b_face = RIGHT;
312                                 break;
313                         }
314                         Maze[y][x] = WALL4;
315                         for (pp = Monitor; pp < End_monitor; pp++)
316                                 check(pp, y, x);
317                         break;
318                   /* Dispersion doors randomly disperse bullets: */
319                   case DOOR:
320                         switch (rand_num(4)) {
321                           case 0:
322                                 bp->b_face = ABOVE;
323                                 break;
324                           case 1:
325                                 bp->b_face = BELOW;
326                                 break;
327                           case 2:
328                                 bp->b_face = LEFTS;
329                                 break;
330                           case 3:
331                                 bp->b_face = RIGHT;
332                                 break;
333                         }
334                         break;
335                   /* Bullets zing past fliers: */
336                   case FLYER:
337                         pp = play_at(y, x);
338                         message(pp, "Zing!");
339                         break;
340                   /* Bullets encountering a player: */
341                   case LEFTS:
342                   case RIGHT:
343                   case BELOW:
344                   case ABOVE:
345                         /*
346                          * Give the person a chance to catch a
347                          * grenade if s/he is facing it:
348                          */
349                         pp = play_at(y, x);
350                         pp->p_ident->i_shot += bp->b_charge;
351                         if (opposite(bp->b_face, Maze[y][x])) {
352                             /* Give them a 10% chance: */
353                             if (rand_num(100) < conf_pgren_catch) {
354                                 /* They caught it! */
355                                 if (bp->b_owner != NULL)
356                                         message(bp->b_owner,
357                                             "Your charge was absorbed!");
358
359                                 /*
360                                  * The target player stole from the bullet's
361                                  * owner. Charge stolen statistics:
362                                  */
363                                 if (bp->b_score != NULL)
364                                         bp->b_score->i_robbed += bp->b_charge;
365
366                                 /* They acquire more ammo: */
367                                 pp->p_ammo += bp->b_charge;
368
369                                 /* Check if it would have destroyed them: */
370                                 if (pp->p_damage + bp->b_size * conf_mindam
371                                     > pp->p_damcap)
372                                         /* Lucky escape statistics: */
373                                         pp->p_ident->i_saved++;
374
375                                 /* Tell them: */
376                                 message(pp, "Absorbed charge (good shield!)");
377
378                                 /* Absorbtion statistics: */
379                                 pp->p_ident->i_absorbed += bp->b_charge;
380
381                                 /* Deallocate storage: */
382                                 free(bp);
383
384                                 /* Update ammo display: */
385                                 ammo_update(pp);
386
387                                 /* No need for caller to keep tracking it: */
388                                 return FALSE;
389                             }
390
391                             /* Bullets faced head-on (statistics): */
392                             pp->p_ident->i_faced += bp->b_charge;
393                         }
394
395                         /*
396                          * Small chance that the bullet just misses the
397                          * person.  If so, the bullet just goes on its
398                          * merry way without exploding. (5% chance)
399                          */
400                         if (rand_num(100) < conf_pmiss) {
401                                 /* Ducked statistics: */
402                                 pp->p_ident->i_ducked += bp->b_charge;
403
404                                 /* Check if it would have killed them: */
405                                 if (pp->p_damage + bp->b_size * conf_mindam
406                                     > pp->p_damcap)
407                                         /* Lucky escape statistics: */
408                                         pp->p_ident->i_saved++;
409
410                                 /* Shooter missed statistics: */
411                                 if (bp->b_score != NULL)
412                                         bp->b_score->i_missed += bp->b_charge;
413
414                                 /* Tell target that they were missed: */
415                                 message(pp, "Zing!");
416
417                                 /* Tell the bullet owner they missed: */
418                                 if (bp->b_owner != NULL)
419                                     message(bp->b_owner,
420                                         ((bp->b_score->i_missed & 0x7) == 0x7) ?
421                                         "My!  What a bad shot you are!" :
422                                         "Missed him");
423
424                                 /* Don't fall through */
425                                 break;
426                         } else {
427                                 /* The player is to be blown up: */
428                                 bp->b_expl = TRUE;
429                         }
430                         break;
431                   /* Bullet hits a wall, and always explodes: */
432                   case WALL1:
433                   case WALL2:
434                   case WALL3:
435                         bp->b_expl = TRUE;
436                         break;
437                 }
438
439                 /* Update the bullet's new position: */
440                 bp->b_x = x;
441                 bp->b_y = y;
442         }
443
444         /* Caller should keep tracking the bullet: */
445         return TRUE;
446 }
447
448 /*
449  * move_drone:
450  *      Move the drone to the next square
451  *      Returns FALSE if the drone need no longer be tracked.
452  */
453 static int
454 move_drone(BULLET *bp)
455 {
456         int     mask, count;
457         int     n, dir = -1;
458         PLAYER  *pp;
459
460         /* See if we can give someone a blast: */
461         if (is_player(Maze[bp->b_y][bp->b_x - 1])) {
462                 dir = WEST;
463                 goto drone_move;
464         }
465         if (is_player(Maze[bp->b_y - 1][bp->b_x])) {
466                 dir = NORTH;
467                 goto drone_move;
468         }
469         if (is_player(Maze[bp->b_y + 1][bp->b_x])) {
470                 dir = SOUTH;
471                 goto drone_move;
472         }
473         if (is_player(Maze[bp->b_y][bp->b_x + 1])) {
474                 dir = EAST;
475                 goto drone_move;
476         }
477
478         /* Find out what directions are clear and move that way: */
479         mask = count = 0;
480         if (!iswall(bp->b_y, bp->b_x - 1))
481                 mask |= WEST, count++;
482         if (!iswall(bp->b_y - 1, bp->b_x))
483                 mask |= NORTH, count++;
484         if (!iswall(bp->b_y + 1, bp->b_x))
485                 mask |= SOUTH, count++;
486         if (!iswall(bp->b_y, bp->b_x + 1))
487                 mask |= EAST, count++;
488
489         /* All blocked up, just wait: */
490         if (count == 0)
491                 return TRUE;
492
493         /* Only one way to go: */
494         if (count == 1) {
495                 dir = mask;
496                 goto drone_move;
497         }
498
499         /* Avoid backtracking, and remove the direction we came from: */
500         switch (bp->b_face) {
501           case LEFTS:
502                 if (mask & EAST)
503                         mask &= ~EAST, count--;
504                 break;
505           case RIGHT:
506                 if (mask & WEST)
507                         mask &= ~WEST, count--;
508                 break;
509           case ABOVE:
510                 if (mask & SOUTH)
511                         mask &= ~SOUTH, count--;
512                 break;
513           case BELOW:
514                 if (mask & NORTH)
515                         mask &= ~NORTH, count--;
516                 break;
517         }
518
519         /* Pick one of the remaining directions: */
520         n = rand_num(count);
521         if (n >= 0 && mask & NORTH)
522                 dir = NORTH, n--;
523         if (n >= 0 && mask & SOUTH)
524                 dir = SOUTH, n--;
525         if (n >= 0 && mask & EAST)
526                 dir = EAST, n--;
527         if (n >= 0 && mask & WEST)
528                 dir = WEST, n--;
529
530 drone_move:
531         /* Move the drone: */
532         switch (dir) {
533           case -1:
534                 /* no move */
535           case WEST:
536                 bp->b_x--;
537                 bp->b_face = LEFTS;
538                 break;
539           case EAST:
540                 bp->b_x++;
541                 bp->b_face = RIGHT;
542                 break;
543           case NORTH:
544                 bp->b_y--;
545                 bp->b_face = ABOVE;
546                 break;
547           case SOUTH:
548                 bp->b_y++;
549                 bp->b_face = BELOW;
550                 break;
551         }
552
553         /* Look at what the drone moved onto: */
554         switch (Maze[bp->b_y][bp->b_x]) {
555           case LEFTS:
556           case RIGHT:
557           case BELOW:
558           case ABOVE:
559                 /*
560                  * Players have a 1% chance of absorbing a drone,
561                  * if they are facing it.
562                  */
563                 if (rand_num(100) < conf_pdroneabsorb && opposite(bp->b_face,
564                     Maze[bp->b_y][bp->b_x])) {
565
566                         /* Feel the power: */
567                         pp = play_at(bp->b_y, bp->b_x);
568                         pp->p_ammo += bp->b_charge;
569                         message(pp, "**** Absorbed drone ****");
570
571                         /* Release drone storage: */
572                         free(bp);
573
574                         /* Update ammo: */
575                         ammo_update(pp);
576
577                         /* No need for caller to keep tracking drone: */
578                         return FALSE;
579                 }
580                 /* Detonate the drone: */
581                 bp->b_expl = TRUE;
582                 break;
583         }
584
585         /* Keep tracking the drone. */
586         return TRUE;
587 }
588
589 /*
590  * save_bullet:
591  *      Put a bullet back onto the bullet list
592  */
593 static void
594 save_bullet(BULLET *bp)
595 {
596
597         /* Save what the bullet will be flying over: */
598         bp->b_over = Maze[bp->b_y][bp->b_x];
599
600         switch (bp->b_over) {
601           /* Bullets that can pass through each other: */
602           case SHOT:
603           case GRENADE:
604           case SATCHEL:
605           case BOMB:
606           case SLIME:
607           case LAVA:
608           case DSHOT:
609                 find_under(Bullets, bp);
610                 break;
611         }
612
613         switch (bp->b_over) {
614           /* A bullet hits a player: */
615           case LEFTS:
616           case RIGHT:
617           case ABOVE:
618           case BELOW:
619           case FLYER:
620                 mark_player(bp);
621                 break;
622
623           /* A bullet passes a boot: */
624           case BOOT:
625           case BOOT_PAIR:
626                 mark_boot(bp);
627                 /* FALLTHROUGH */
628
629           /* The bullet flies over everything else: */
630           default:
631                 Maze[bp->b_y][bp->b_x] = bp->b_type;
632                 break;
633         }
634
635         /* Insert the bullet into the Bullets list: */
636         bp->b_next = Bullets;
637         Bullets = bp;
638 }
639
640 /*
641  * move_flyer:
642  *      Update the position of a player in flight
643  */
644 static void
645 move_flyer(PLAYER *pp)
646 {
647         int     x, y;
648
649         if (pp->p_undershot) {
650                 fixshots(pp->p_y, pp->p_x, pp->p_over);
651                 pp->p_undershot = FALSE;
652         }
653
654         /* Restore what the flier was flying over */
655         Maze[pp->p_y][pp->p_x] = pp->p_over;
656
657         /* Fly: */
658         x = pp->p_x + pp->p_flyx;
659         y = pp->p_y + pp->p_flyy;
660
661         /* Bouncing off the edges of the maze: */
662         if (x < 1) {
663                 x = 1 - x;
664                 pp->p_flyx = -pp->p_flyx;
665         }
666         else if (x > WIDTH - 2) {
667                 x = (WIDTH - 2) - (x - (WIDTH - 2));
668                 pp->p_flyx = -pp->p_flyx;
669         }
670         if (y < 1) {
671                 y = 1 - y;
672                 pp->p_flyy = -pp->p_flyy;
673         }
674         else if (y > HEIGHT - 2) {
675                 y = (HEIGHT - 2) - (y - (HEIGHT - 2));
676                 pp->p_flyy = -pp->p_flyy;
677         }
678
679         /* Make sure we don't land on something we can't: */
680 again:
681         switch (Maze[y][x]) {
682           default:
683                 /*
684                  * Flier is over something other than space, a wall
685                  * or a door. Randomly move (drift) the flier a little bit
686                  * and then try again:
687                  */
688                 switch (rand_num(4)) {
689                   case 0:
690                         PLUS_DELTA(x, WIDTH - 2);
691                         break;
692                   case 1:
693                         MINUS_DELTA(x, 1);
694                         break;
695                   case 2:
696                         PLUS_DELTA(y, HEIGHT - 2);
697                         break;
698                   case 3:
699                         MINUS_DELTA(y, 1);
700                         break;
701                 }
702                 goto again;
703           /* Give a little boost when about to land on a wall or door: */
704           case WALL1:
705           case WALL2:
706           case WALL3:
707           case WALL4:
708           case WALL5:
709           case DOOR:
710                 if (pp->p_flying == 0)
711                         pp->p_flying++;
712                 break;
713           /* Spaces are okay: */
714           case SPACE:
715                 break;
716         }
717
718         /* Update flier's coordinates: */
719         pp->p_y = y;
720         pp->p_x = x;
721
722         /* Consume 'flying' time: */
723         if (pp->p_flying-- == 0) {
724                 /* Land: */
725                 if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
726                         /* Land a player - they stustain a fall: */
727                         checkdam(pp, NULL, NULL,
728                                 rand_num(pp->p_damage / conf_fall_frac), FALL);
729                         pp->p_face = rand_dir();
730                         showstat(pp);
731                 } else {
732                         /* Land boots: */
733                         if (Maze[y][x] == BOOT)
734                                 pp->p_face = BOOT_PAIR;
735                         Maze[y][x] = SPACE;
736                 }
737         }
738
739         /* Save under the flier: */
740         pp->p_over = Maze[y][x];
741         /* Draw in the flier: */
742         Maze[y][x] = pp->p_face;
743         showexpl(y, x, pp->p_face);
744 }
745
746 /*
747  * chkshot
748  *      Handle explosions
749  */
750 static void
751 chkshot(BULLET *bp, BULLET *next)
752 {
753         int     y, x;
754         int     dy, dx, absdy;
755         int     delta, damage;
756         char    expl;
757         PLAYER  *pp;
758
759         delta = 0;
760         switch (bp->b_type) {
761           case SHOT:
762           case MINE:
763           case GRENADE:
764           case GMINE:
765           case SATCHEL:
766           case BOMB:
767                 delta = bp->b_size - 1;
768                 break;
769           case SLIME:
770           case LAVA:
771                 chkslime(bp, next);
772                 return;
773           case DSHOT:
774                 bp->b_type = SLIME;
775                 chkslime(bp, next);
776                 return;
777         }
778
779         /* Draw the explosion square: */
780         for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
781                 if (y < 0 || y >= HEIGHT)
782                         continue;
783                 dy = y - bp->b_y;
784                 absdy = (dy < 0) ? -dy : dy;
785                 for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
786                         /* Draw a part of the explosion cloud: */
787                         if (x < 0 || x >= WIDTH)
788                                 continue;
789                         dx = x - bp->b_x;
790                         if (dx == 0)
791                                 expl = (dy == 0) ? '*' : '|';
792                         else if (dy == 0)
793                                 expl = '-';
794                         else if (dx == dy)
795                                 expl = '\\';
796                         else if (dx == -dy)
797                                 expl = '/';
798                         else
799                                 expl = '*';
800                         showexpl(y, x, expl);
801
802                         /* Check what poor bastard was in the explosion: */
803                         switch (Maze[y][x]) {
804                           case LEFTS:
805                           case RIGHT:
806                           case ABOVE:
807                           case BELOW:
808                           case FLYER:
809                                 if (dx < 0)
810                                         dx = -dx;
811                                 if (absdy > dx)
812                                         damage = bp->b_size - absdy;
813                                 else
814                                         damage = bp->b_size - dx;
815
816                                 /* Everybody hurts, sometimes. */
817                                 pp = play_at(y, x);
818                                 checkdam(pp, bp->b_owner, bp->b_score,
819                                         damage * conf_mindam, bp->b_type);
820                                 break;
821                           case GMINE:
822                           case MINE:
823                                 /* Mines detonate in a chain reaction: */
824                                 add_shot((Maze[y][x] == GMINE) ?
825                                         GRENADE : SHOT,
826                                         y, x, LEFTS,
827                                         (Maze[y][x] == GMINE) ?
828                                         GRENREQ : BULREQ,
829                                         NULL, TRUE, SPACE);
830                                 Maze[y][x] = SPACE;
831                                 break;
832                         }
833                 }
834         }
835 }
836
837 /*
838  * chkslime:
839  *      handle slime shot exploding
840  */
841 static void
842 chkslime(BULLET *bp, BULLET *next)
843 {
844         BULLET  *nbp;
845
846         switch (Maze[bp->b_y][bp->b_x]) {
847           /* Slime explodes on walls and doors: */
848           case WALL1:
849           case WALL2:
850           case WALL3:
851           case WALL4:
852           case WALL5:
853           case DOOR:
854                 switch (bp->b_face) {
855                   case LEFTS:
856                         bp->b_x++;
857                         break;
858                   case RIGHT:
859                         bp->b_x--;
860                         break;
861                   case ABOVE:
862                         bp->b_y++;
863                         break;
864                   case BELOW:
865                         bp->b_y--;
866                         break;
867                 }
868                 break;
869         }
870
871         /* Duplicate the unit of slime: */
872         nbp = (BULLET *) malloc(sizeof (BULLET));
873         if (nbp == NULL) {
874                 logit(LOG_ERR, "malloc");
875                 return;
876         }
877         *nbp = *bp;
878
879         /* Move it around: */
880         move_slime(nbp, nbp->b_type == SLIME ? conf_slimespeed :
881             conf_lavaspeed, next);
882 }
883
884 /*
885  * move_slime:
886  *      move the given slime shot speed times and add it back if
887  *      it hasn't fizzled yet
888  */
889 static void
890 move_slime(BULLET *bp, int speed, BULLET *next)
891 {
892         int     i, j, dirmask, count;
893         PLAYER  *pp;
894         BULLET  *nbp;
895
896         if (speed == 0) {
897                 if (bp->b_charge <= 0)
898                         free(bp);
899                 else
900                         save_bullet(bp);
901                 return;
902         }
903
904         /* Draw it: */
905         showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
906
907         switch (Maze[bp->b_y][bp->b_x]) {
908           /* Someone got hit by slime or lava: */
909           case LEFTS:
910           case RIGHT:
911           case ABOVE:
912           case BELOW:
913           case FLYER:
914                 pp = play_at(bp->b_y, bp->b_x);
915                 message(pp, "You've been slimed.");
916                 checkdam(pp, bp->b_owner, bp->b_score, conf_mindam, bp->b_type);
917                 break;
918           /* Bullets detonate in slime and lava: */
919           case SHOT:
920           case GRENADE:
921           case SATCHEL:
922           case BOMB:
923           case DSHOT:
924                 explshot(next, bp->b_y, bp->b_x);
925                 explshot(Bullets, bp->b_y, bp->b_x);
926                 break;
927         }
928
929
930         /* Drain the slime/lava of some energy: */
931         if (--bp->b_charge <= 0) {
932                 /* It fizzled: */
933                 free(bp);
934                 return;
935         }
936
937         /* Figure out which way the slime should flow: */
938         dirmask = 0;
939         count = 0;
940         switch (bp->b_face) {
941           case LEFTS:
942                 if (!iswall(bp->b_y, bp->b_x - 1))
943                         dirmask |= WEST, count++;
944                 if (!iswall(bp->b_y - 1, bp->b_x))
945                         dirmask |= NORTH, count++;
946                 if (!iswall(bp->b_y + 1, bp->b_x))
947                         dirmask |= SOUTH, count++;
948                 if (dirmask == 0)
949                         if (!iswall(bp->b_y, bp->b_x + 1))
950                                 dirmask |= EAST, count++;
951                 break;
952           case RIGHT:
953                 if (!iswall(bp->b_y, bp->b_x + 1))
954                         dirmask |= EAST, count++;
955                 if (!iswall(bp->b_y - 1, bp->b_x))
956                         dirmask |= NORTH, count++;
957                 if (!iswall(bp->b_y + 1, bp->b_x))
958                         dirmask |= SOUTH, count++;
959                 if (dirmask == 0)
960                         if (!iswall(bp->b_y, bp->b_x - 1))
961                                 dirmask |= WEST, count++;
962                 break;
963           case ABOVE:
964                 if (!iswall(bp->b_y - 1, bp->b_x))
965                         dirmask |= NORTH, count++;
966                 if (!iswall(bp->b_y, bp->b_x - 1))
967                         dirmask |= WEST, count++;
968                 if (!iswall(bp->b_y, bp->b_x + 1))
969                         dirmask |= EAST, count++;
970                 if (dirmask == 0)
971                         if (!iswall(bp->b_y + 1, bp->b_x))
972                                 dirmask |= SOUTH, count++;
973                 break;
974           case BELOW:
975                 if (!iswall(bp->b_y + 1, bp->b_x))
976                         dirmask |= SOUTH, count++;
977                 if (!iswall(bp->b_y, bp->b_x - 1))
978                         dirmask |= WEST, count++;
979                 if (!iswall(bp->b_y, bp->b_x + 1))
980                         dirmask |= EAST, count++;
981                 if (dirmask == 0)
982                         if (!iswall(bp->b_y - 1, bp->b_x))
983                                 dirmask |= NORTH, count++;
984                 break;
985         }
986         if (count == 0) {
987                 /*
988                  * No place to go.  Just sit here for a while and wait
989                  * for adjacent squares to clear out.
990                  */
991                 save_bullet(bp);
992                 return;
993         }
994         if (bp->b_charge < count) {
995                 /* Only bp->b_charge paths may be taken */
996                 while (count > bp->b_charge) {
997                         if (dirmask & WEST)
998                                 dirmask &= ~WEST;
999                         else if (dirmask & EAST)
1000                                 dirmask &= ~EAST;
1001                         else if (dirmask & NORTH)
1002                                 dirmask &= ~NORTH;
1003                         else if (dirmask & SOUTH)
1004                                 dirmask &= ~SOUTH;
1005                         count--;
1006                 }
1007         }
1008
1009         /* Spawn little slimes off in every possible direction: */
1010         i = bp->b_charge / count;
1011         j = bp->b_charge % count;
1012         if (dirmask & WEST) {
1013                 count--;
1014                 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
1015                         i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE);
1016                 move_slime(nbp, speed - 1, next);
1017         }
1018         if (dirmask & EAST) {
1019                 count--;
1020                 nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
1021                         (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
1022                         bp->b_score, TRUE, SPACE);
1023                 move_slime(nbp, speed - 1, next);
1024         }
1025         if (dirmask & NORTH) {
1026                 count--;
1027                 nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
1028                         (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
1029                         bp->b_score, TRUE, SPACE);
1030                 move_slime(nbp, speed - 1, next);
1031         }
1032         if (dirmask & SOUTH) {
1033                 count--;
1034                 nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
1035                         (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
1036                         bp->b_score, TRUE, SPACE);
1037                 move_slime(nbp, speed - 1, next);
1038         }
1039
1040         free(bp);
1041 }
1042
1043 /*
1044  * iswall:
1045  *      returns whether the given location is a wall
1046  */
1047 static int
1048 iswall(int y, int x)
1049 {
1050         if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
1051                 return TRUE;
1052         switch (Maze[y][x]) {
1053           case WALL1:
1054           case WALL2:
1055           case WALL3:
1056           case WALL4:
1057           case WALL5:
1058           case DOOR:
1059           case SLIME:
1060           case LAVA:
1061                 return TRUE;
1062         }
1063         return FALSE;
1064 }
1065
1066 /*
1067  * zapshot:
1068  *      Take a shot out of the air.
1069  */
1070 static void
1071 zapshot(BULLET *blist, BULLET *obp)
1072 {
1073         BULLET  *bp;
1074
1075         for (bp = blist; bp != NULL; bp = bp->b_next) {
1076                 /* Find co-located bullets not facing the same way: */
1077                 if (bp->b_face != obp->b_face
1078                     && bp->b_x == obp->b_x && bp->b_y == obp->b_y)
1079                 {
1080                         /* Bullet collision: */
1081                         explshot(blist, obp->b_y, obp->b_x);
1082                         return;
1083                 }
1084         }
1085 }
1086
1087 /*
1088  * explshot -
1089  *      Make all shots at this location blow up
1090  */
1091 static void
1092 explshot(BULLET *blist, int y, int x)
1093 {
1094         BULLET  *bp;
1095
1096         for (bp = blist; bp != NULL; bp = bp->b_next)
1097                 if (bp->b_x == x && bp->b_y == y) {
1098                         bp->b_expl = TRUE;
1099                         if (bp->b_owner != NULL)
1100                                 message(bp->b_owner, "Shot intercepted.");
1101                 }
1102 }
1103
1104 /*
1105  * play_at:
1106  *      Return a pointer to the player at the given location
1107  */
1108 PLAYER *
1109 play_at(int y, int x)
1110 {
1111         PLAYER  *pp;
1112
1113         for (pp = Player; pp < End_player; pp++)
1114                 if (pp->p_x == x && pp->p_y == y)
1115                         return pp;
1116
1117         /* Internal fault: */
1118         logx(LOG_ERR, "play_at: not a player");
1119         abort();
1120 }
1121
1122 /*
1123  * opposite:
1124  *      Return TRUE if the bullet direction faces the opposite direction
1125  *      of the player in the maze
1126  */
1127 int
1128 opposite(int face, char dir)
1129 {
1130         switch (face) {
1131           case LEFTS:
1132                 return (dir == RIGHT);
1133           case RIGHT:
1134                 return (dir == LEFTS);
1135           case ABOVE:
1136                 return (dir == BELOW);
1137           case BELOW:
1138                 return (dir == ABOVE);
1139           default:
1140                 return FALSE;
1141         }
1142 }
1143
1144 /*
1145  * is_bullet:
1146  *      Is there a bullet at the given coordinates?  If so, return
1147  *      a pointer to the bullet, otherwise return NULL
1148  */
1149 BULLET *
1150 is_bullet(int y, int x)
1151 {
1152         BULLET  *bp;
1153
1154         for (bp = Bullets; bp != NULL; bp = bp->b_next)
1155                 if (bp->b_y == y && bp->b_x == x)
1156                         return bp;
1157         return NULL;
1158 }
1159
1160 /*
1161  * fixshots:
1162  *      change the underlying character of the shots at a location
1163  *      to the given character.
1164  */
1165 void
1166 fixshots(int y, int x, char over)
1167 {
1168         BULLET  *bp;
1169
1170         for (bp = Bullets; bp != NULL; bp = bp->b_next)
1171                 if (bp->b_y == y && bp->b_x == x)
1172                         bp->b_over = over;
1173 }
1174
1175 /*
1176  * find_under:
1177  *      find the underlying character for a bullet when it lands
1178  *      on another bullet.
1179  */
1180 static void
1181 find_under(BULLET *blist, BULLET *bp)
1182 {
1183         BULLET  *nbp;
1184
1185         for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
1186                 if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
1187                         bp->b_over = nbp->b_over;
1188                         break;
1189                 }
1190 }
1191
1192 /*
1193  * mark_player:
1194  *      mark a player as under a shot
1195  */
1196 static void
1197 mark_player(BULLET *bp)
1198 {
1199         PLAYER  *pp;
1200
1201         for (pp = Player; pp < End_player; pp++)
1202                 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
1203                         pp->p_undershot = TRUE;
1204                         break;
1205                 }
1206 }
1207
1208 /*
1209  * mark_boot:
1210  *      mark a boot as under a shot
1211  */
1212 static void
1213 mark_boot(BULLET *bp)
1214 {
1215         PLAYER  *pp;
1216
1217         for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
1218                 if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
1219                         pp->p_undershot = TRUE;
1220                         break;
1221                 }
1222 }