1 /* $NetBSD: shots.c,v 1.10 2009/07/04 04:29:55 dholland Exp $ */
3 * Copyright (c) 1983-2003, Regents of the University of California.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
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
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.
33 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: shots.c,v 1.10 2009/07/04 04:29:55 dholland Exp $");
43 #define PLUS_DELTA(x, max) if (x < max) x++; else x--
44 #define MINUS_DELTA(x, min) if (x > min) x--; else x++
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 int iswall(int, int);
51 static void mark_boot(BULLET
*);
52 static void mark_player(BULLET
*);
54 static void move_drone(BULLET
*);
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
*);
64 * Move the shots already in the air, taking explosions into account
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.
87 for (bp
= blist
; bp
!= NULL
; bp
= next
) {
91 Maze
[y
][x
] = bp
->b_over
;
92 for (pp
= Player
; pp
< End_player
; pp
++)
95 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
104 if (move_normal_shot(bp
)) {
105 bp
->b_next
= Bullets
;
111 if (bp
->b_expl
|| move_normal_shot(bp
)) {
112 bp
->b_next
= Bullets
;
119 if (move_drone(bp
)) {
120 bp
->b_next
= Bullets
;
126 bp
->b_next
= Bullets
;
134 for (bp
= blist
; bp
!= NULL
; bp
= next
) {
139 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
140 check(pp
, bp
->b_y
, bp
->b_x
);
143 if (bp
->b_type
== DSHOT
)
144 for (pp
= Player
; pp
< End_player
; pp
++)
146 check(pp
, bp
->b_y
, bp
->b_x
);
155 for (pp
= Player
; pp
< End_player
; pp
++)
156 Maze
[pp
->p_y
][pp
->p_x
] = pp
->p_face
;
160 for (pp
= Boot
; pp
< &Boot
[NBOOTS
]; pp
++)
161 if (pp
->p_flying
>= 0)
164 for (pp
= Player
; pp
< End_player
; pp
++) {
166 if (pp
->p_flying
>= 0)
169 sendcom(pp
, REFRESH
); /* Flush out the explosions */
171 sendcom(pp
, REFRESH
);
174 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
175 sendcom(pp
, REFRESH
);
183 * Move a normal shot along its trajectory
186 move_normal_shot(BULLET
*bp
)
191 for (i
= 0; i
< BULSPD
; i
++) {
198 switch (bp
->b_face
) {
213 switch (Maze
[y
][x
]) {
215 if (rand_num(100) < 5) {
216 zapshot(Bullets
, bp
);
217 zapshot(bp
->b_next
, bp
);
221 if (rand_num(100) < 10) {
222 zapshot(Bullets
, bp
);
223 zapshot(bp
->b_next
, bp
);
227 case WALL4
: /* reflecting walls */
228 switch (bp
->b_face
) {
244 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
249 switch (bp
->b_face
) {
265 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
272 switch (rand_num(4)) {
291 message(pp
, "Zing!");
299 * give the person a chance to catch a
300 * grenade if s/he is facing it
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
)
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
314 pp
->p_ident
->i_saved
++;
315 message(pp
, "Absorbed charge (good shield!)");
316 pp
->p_ident
->i_absorbed
+= bp
->b_charge
;
318 (void) snprintf(Buf
, sizeof(Buf
),
320 cgoto(pp
, STAT_AMMO_ROW
, STAT_VALUE_COL
);
324 pp
->p_ident
->i_faced
+= bp
->b_charge
;
327 * Small chance that the bullet just misses the
328 * person. If so, the bullet just goes on its
329 * merry way without exploding.
331 if (rand_num(100) < 5) {
332 pp
->p_ident
->i_ducked
+= bp
->b_charge
;
333 if (pp
->p_damage
+ bp
->b_size
* MINDAM
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
)
341 message(bp
->b_owner
, bp
->b_score
&&
342 ((bp
->b_score
->i_missed
& 0x7) == 0x7) ?
343 "My! What a bad shot you are!" :
348 * The shot hit that sucker! Blow it up.
370 * Move the drone to the next square
373 move_drone(BULLET
*bp
)
380 * See if we can give someone a blast
382 if (isplayer(Maze
[bp
->b_y
][bp
->b_x
- 1])) {
386 if (isplayer(Maze
[bp
->b_y
- 1][bp
->b_x
])) {
390 if (isplayer(Maze
[bp
->b_y
+ 1][bp
->b_x
])) {
394 if (isplayer(Maze
[bp
->b_y
][bp
->b_x
+ 1])) {
400 * Find out what directions are clear
403 if (!iswall(bp
->b_y
, bp
->b_x
- 1))
404 mask
|= WEST
, count
++;
405 if (!iswall(bp
->b_y
- 1, bp
->b_x
))
406 mask
|= NORTH
, count
++;
407 if (!iswall(bp
->b_y
+ 1, bp
->b_x
))
408 mask
|= SOUTH
, count
++;
409 if (!iswall(bp
->b_y
, bp
->b_x
+ 1))
410 mask
|= EAST
, count
++;
413 * All blocked up, just you wait
419 * Only one way to go.
427 * Get rid of the direction that we came from
429 switch (bp
->b_face
) {
432 mask
&= ~EAST
, count
--;
436 mask
&= ~WEST
, count
--;
440 mask
&= ~SOUTH
, count
--;
444 mask
&= ~NORTH
, count
--;
449 * Pick one of the remaining directions
452 if (n
>= 0 && mask
& NORTH
)
454 if (n
>= 0 && mask
& SOUTH
)
456 if (n
>= 0 && mask
& EAST
)
458 if (n
>= 0 && mask
& WEST
)
462 * Now that we know the direction of movement,
463 * just update the position of the drone
484 switch (Maze
[bp
->b_y
][bp
->b_x
]) {
490 * give the person a chance to catch a
491 * drone if s/he is facing it
493 if (rand_num(100) < 1 &&
494 opposite(bp
->b_face
, Maze
[bp
->b_y
][bp
->b_x
])) {
495 pp
= play_at(bp
->b_y
, bp
->b_x
);
496 pp
->p_ammo
+= bp
->b_charge
;
497 message(pp
, "**** Absorbed drone ****");
499 (void) snprintf(Buf
, sizeof(buf
), "%3d", pp
->p_ammo
);
500 cgoto(pp
, STAT_AMMO_ROW
, STAT_VALUE_COL
);
513 * Put this bullet back onto the bullet list
516 save_bullet(BULLET
*bp
)
518 bp
->b_over
= Maze
[bp
->b_y
][bp
->b_x
];
519 switch (bp
->b_over
) {
533 find_under(Bullets
, bp
);
537 switch (bp
->b_over
) {
554 Maze
[bp
->b_y
][bp
->b_x
] = bp
->b_type
;
558 bp
->b_next
= Bullets
;
564 * Update the position of a player in flight
567 move_flyer(PLAYER
*pp
)
571 if (pp
->p_undershot
) {
572 fixshots(pp
->p_y
, pp
->p_x
, pp
->p_over
);
573 pp
->p_undershot
= FALSE
;
575 Maze
[pp
->p_y
][pp
->p_x
] = pp
->p_over
;
576 x
= pp
->p_x
+ pp
->p_flyx
;
577 y
= pp
->p_y
+ pp
->p_flyy
;
580 pp
->p_flyx
= -pp
->p_flyx
;
582 else if (x
> WIDTH
- 2) {
583 x
= (WIDTH
- 2) - (x
- (WIDTH
- 2));
584 pp
->p_flyx
= -pp
->p_flyx
;
588 pp
->p_flyy
= -pp
->p_flyy
;
590 else if (y
> HEIGHT
- 2) {
591 y
= (HEIGHT
- 2) - (y
- (HEIGHT
- 2));
592 pp
->p_flyy
= -pp
->p_flyy
;
595 switch (Maze
[y
][x
]) {
597 switch (rand_num(4)) {
599 PLUS_DELTA(x
, WIDTH
- 2);
605 PLUS_DELTA(y
, HEIGHT
- 2);
622 if (pp
->p_flying
== 0)
630 if (pp
->p_flying
-- == 0) {
632 if (pp
->p_face
!= BOOT
&& pp
->p_face
!= BOOT_PAIR
) {
634 checkdam(pp
, (PLAYER
*) NULL
, (IDENT
*) NULL
,
635 rand_num(pp
->p_damage
/ 5), FALL
);
636 pp
->p_face
= rand_dir();
641 if (Maze
[y
][x
] == BOOT
)
642 pp
->p_face
= BOOT_PAIR
;
647 pp
->p_over
= Maze
[y
][x
];
648 Maze
[y
][x
] = pp
->p_face
;
649 showexpl(y
, x
, pp
->p_face
);
657 chkshot(BULLET
*bp
, BULLET
*next
)
666 switch (bp
->b_type
) {
673 delta
= bp
->b_size
- 1;
690 for (y
= bp
->b_y
- delta
; y
<= bp
->b_y
+ delta
; y
++) {
691 if (y
< 0 || y
>= HEIGHT
)
694 absdy
= (dy
< 0) ? -dy
: dy
;
695 for (x
= bp
->b_x
- delta
; x
<= bp
->b_x
+ delta
; x
++) {
696 if (x
< 0 || x
>= WIDTH
)
700 expl
= (dy
== 0) ? '*' : '|';
709 showexpl(y
, x
, expl
);
710 switch (Maze
[y
][x
]) {
721 damage
= bp
->b_size
- absdy
;
723 damage
= bp
->b_size
- dx
;
725 checkdam(pp
, bp
->b_owner
, bp
->b_score
,
726 damage
* MINDAM
, bp
->b_type
);
730 add_shot((Maze
[y
][x
] == GMINE
) ?
733 (Maze
[y
][x
] == GMINE
) ?
735 (PLAYER
*) NULL
, TRUE
, SPACE
);
746 * handle slime shot exploding
749 chkslime(BULLET
*bp
, BULLET
*next
)
753 switch (Maze
[bp
->b_y
][bp
->b_x
]) {
764 switch (bp
->b_face
) {
780 nbp
= malloc(sizeof(*nbp
));
783 move_slime(nbp
, nbp
->b_type
== SLIME
? SLIMESPEED
: LAVASPEED
, next
);
785 move_slime(nbp
, SLIMESPEED
, next
);
791 * move the given slime shot speed times and add it back if
792 * it hasn't fizzled yet
795 move_slime(BULLET
*bp
, int speed
, BULLET
*next
)
797 int i
, j
, dirmask
, count
;
802 if (bp
->b_charge
<= 0)
810 showexpl(bp
->b_y
, bp
->b_x
, bp
->b_type
== LAVA
? LAVA
: '*');
812 showexpl(bp
->b_y
, bp
->b_x
, '*');
814 switch (Maze
[bp
->b_y
][bp
->b_x
]) {
822 pp
= play_at(bp
->b_y
, bp
->b_x
);
823 message(pp
, "You've been slimed.");
824 checkdam(pp
, bp
->b_owner
, bp
->b_score
, MINDAM
, bp
->b_type
);
833 explshot(next
, bp
->b_y
, bp
->b_x
);
834 explshot(Bullets
, bp
->b_y
, bp
->b_x
);
838 if (--bp
->b_charge
<= 0) {
845 switch (bp
->b_face
) {
847 if (!iswall(bp
->b_y
, bp
->b_x
- 1))
848 dirmask
|= WEST
, count
++;
849 if (!iswall(bp
->b_y
- 1, bp
->b_x
))
850 dirmask
|= NORTH
, count
++;
851 if (!iswall(bp
->b_y
+ 1, bp
->b_x
))
852 dirmask
|= SOUTH
, count
++;
854 if (!iswall(bp
->b_y
, bp
->b_x
+ 1))
855 dirmask
|= EAST
, count
++;
858 if (!iswall(bp
->b_y
, bp
->b_x
+ 1))
859 dirmask
|= EAST
, count
++;
860 if (!iswall(bp
->b_y
- 1, bp
->b_x
))
861 dirmask
|= NORTH
, count
++;
862 if (!iswall(bp
->b_y
+ 1, bp
->b_x
))
863 dirmask
|= SOUTH
, count
++;
865 if (!iswall(bp
->b_y
, bp
->b_x
- 1))
866 dirmask
|= WEST
, count
++;
869 if (!iswall(bp
->b_y
- 1, bp
->b_x
))
870 dirmask
|= NORTH
, count
++;
871 if (!iswall(bp
->b_y
, bp
->b_x
- 1))
872 dirmask
|= WEST
, count
++;
873 if (!iswall(bp
->b_y
, bp
->b_x
+ 1))
874 dirmask
|= EAST
, count
++;
876 if (!iswall(bp
->b_y
+ 1, bp
->b_x
))
877 dirmask
|= SOUTH
, count
++;
880 if (!iswall(bp
->b_y
+ 1, bp
->b_x
))
881 dirmask
|= SOUTH
, count
++;
882 if (!iswall(bp
->b_y
, bp
->b_x
- 1))
883 dirmask
|= WEST
, count
++;
884 if (!iswall(bp
->b_y
, bp
->b_x
+ 1))
885 dirmask
|= EAST
, count
++;
887 if (!iswall(bp
->b_y
- 1, bp
->b_x
))
888 dirmask
|= NORTH
, count
++;
893 * No place to go. Just sit here for a while and wait
894 * for adjacent squares to clear out.
899 if (bp
->b_charge
< count
) {
900 /* Only bp->b_charge paths may be taken */
901 while (count
> bp
->b_charge
) {
904 else if (dirmask
& EAST
)
906 else if (dirmask
& NORTH
)
908 else if (dirmask
& SOUTH
)
914 i
= bp
->b_charge
/ count
;
915 j
= bp
->b_charge
% count
;
916 if (dirmask
& WEST
) {
918 nbp
= create_shot(bp
->b_type
, bp
->b_y
, bp
->b_x
- 1, LEFTS
,
919 i
, bp
->b_size
, bp
->b_owner
, bp
->b_score
, TRUE
, SPACE
);
920 move_slime(nbp
, speed
- 1, next
);
922 if (dirmask
& EAST
) {
924 nbp
= create_shot(bp
->b_type
, bp
->b_y
, bp
->b_x
+ 1, RIGHT
,
925 (count
< j
) ? i
+ 1 : i
, bp
->b_size
, bp
->b_owner
,
926 bp
->b_score
, TRUE
, SPACE
);
927 move_slime(nbp
, speed
- 1, next
);
929 if (dirmask
& NORTH
) {
931 nbp
= create_shot(bp
->b_type
, bp
->b_y
- 1, bp
->b_x
, ABOVE
,
932 (count
< j
) ? i
+ 1 : i
, bp
->b_size
, bp
->b_owner
,
933 bp
->b_score
, TRUE
, SPACE
);
934 move_slime(nbp
, speed
- 1, next
);
936 if (dirmask
& SOUTH
) {
938 nbp
= create_shot(bp
->b_type
, bp
->b_y
+ 1, bp
->b_x
, BELOW
,
939 (count
< j
) ? i
+ 1 : i
, bp
->b_size
, bp
->b_owner
,
940 bp
->b_score
, TRUE
, SPACE
);
941 move_slime(nbp
, speed
- 1, next
);
949 * returns whether the given location is a wall
954 if (y
< 0 || x
< 0 || y
>= HEIGHT
|| x
>= WIDTH
)
956 switch (Maze
[y
][x
]) {
981 * Take a shot out of the air.
984 zapshot(BULLET
*blist
, BULLET
*obp
)
990 for (bp
= blist
; bp
!= NULL
; bp
= bp
->b_next
) {
991 if (bp
->b_x
!= obp
->b_x
|| bp
->b_y
!= obp
->b_y
)
993 if (bp
->b_face
== obp
->b_face
)
1000 explshot(blist
, obp
->b_y
, obp
->b_x
);
1005 * Make all shots at this location blow up
1008 explshot(BULLET
*blist
, int y
, int x
)
1012 for (bp
= blist
; bp
!= NULL
; bp
= bp
->b_next
)
1013 if (bp
->b_x
== x
&& bp
->b_y
== y
) {
1015 if (bp
->b_owner
!= NULL
)
1016 message(bp
->b_owner
, "Shot intercepted");
1022 * Return a pointer to the player at the given location
1025 play_at(int y
, int x
)
1029 for (pp
= Player
; pp
< End_player
; pp
++)
1030 if (pp
->p_x
== x
&& pp
->p_y
== y
)
1032 errx(1, "driver: couldn't find player at (%d,%d)", x
, y
);
1038 * Return TRUE if the bullet direction faces the opposite direction
1039 * of the player in the maze
1042 opposite(int face
, char dir
)
1046 return (dir
== RIGHT
);
1048 return (dir
== LEFTS
);
1050 return (dir
== BELOW
);
1052 return (dir
== ABOVE
);
1060 * Is there a bullet at the given coordinates? If so, return
1061 * a pointer to the bullet, otherwise return NULL
1064 is_bullet(int y
, int x
)
1068 for (bp
= Bullets
; bp
!= NULL
; bp
= bp
->b_next
)
1069 if (bp
->b_y
== y
&& bp
->b_x
== x
)
1076 * change the underlying character of the shots at a location
1077 * to the given character.
1080 fixshots(int y
, int x
, char over
)
1084 for (bp
= Bullets
; bp
!= NULL
; bp
= bp
->b_next
)
1085 if (bp
->b_y
== y
&& bp
->b_x
== x
)
1091 * find the underlying character for a bullet when it lands
1092 * on another bullet.
1095 find_under(BULLET
*blist
, BULLET
*bp
)
1099 for (nbp
= blist
; nbp
!= NULL
; nbp
= nbp
->b_next
)
1100 if (bp
->b_y
== nbp
->b_y
&& bp
->b_x
== nbp
->b_x
) {
1101 bp
->b_over
= nbp
->b_over
;
1108 * mark a player as under a shot
1111 mark_player(BULLET
*bp
)
1115 for (pp
= Player
; pp
< End_player
; pp
++)
1116 if (pp
->p_y
== bp
->b_y
&& pp
->p_x
== bp
->b_x
) {
1117 pp
->p_undershot
= TRUE
;
1125 * mark a boot as under a shot
1128 mark_boot(BULLET
*bp
)
1132 for (pp
= Boot
; pp
< &Boot
[NBOOTS
]; pp
++)
1133 if (pp
->p_y
== bp
->b_y
&& pp
->p_x
== bp
->b_x
) {
1134 pp
->p_undershot
= TRUE
;