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