xref: /openbsd/games/hunt/huntd/shots.c (revision ac1fa6a8)
1*ac1fa6a8Skrw /*	$OpenBSD: shots.c,v 1.14 2017/01/21 08:22:57 krw Exp $	*/
23faf6791Sd /*	$NetBSD: shots.c,v 1.3 1997/10/11 08:13:50 lukem Exp $	*/
33faf6791Sd /*
4598075eaSpjanzen  * Copyright (c) 1983-2003, Regents of the University of California.
5598075eaSpjanzen  * All rights reserved.
6598075eaSpjanzen  *
7598075eaSpjanzen  * Redistribution and use in source and binary forms, with or without
8598075eaSpjanzen  * modification, are permitted provided that the following conditions are
9598075eaSpjanzen  * met:
10598075eaSpjanzen  *
11598075eaSpjanzen  * + Redistributions of source code must retain the above copyright
12598075eaSpjanzen  *   notice, this list of conditions and the following disclaimer.
13598075eaSpjanzen  * + Redistributions in binary form must reproduce the above copyright
14598075eaSpjanzen  *   notice, this list of conditions and the following disclaimer in the
15598075eaSpjanzen  *   documentation and/or other materials provided with the distribution.
16598075eaSpjanzen  * + Neither the name of the University of California, San Francisco nor
17598075eaSpjanzen  *   the names of its contributors may be used to endorse or promote
18598075eaSpjanzen  *   products derived from this software without specific prior written
19598075eaSpjanzen  *   permission.
20598075eaSpjanzen  *
21598075eaSpjanzen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22598075eaSpjanzen  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23598075eaSpjanzen  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24598075eaSpjanzen  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25598075eaSpjanzen  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26598075eaSpjanzen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27598075eaSpjanzen  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28598075eaSpjanzen  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29598075eaSpjanzen  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30598075eaSpjanzen  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31598075eaSpjanzen  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
323faf6791Sd  */
333faf6791Sd 
34e29f8a1bSguenther #include <sys/select.h>
353faf6791Sd #include <stdlib.h>
36fab1dce0Sd #include <syslog.h>
379ef48543Smestre 
38fab1dce0Sd #include "conf.h"
399ef48543Smestre #include "hunt.h"
40fab1dce0Sd #include "server.h"
413faf6791Sd 
423faf6791Sd #define	PLUS_DELTA(x, max)	if (x < max) x++; else x--
433faf6791Sd #define	MINUS_DELTA(x, min)	if (x > min) x--; else x++
443faf6791Sd 
45c72b5b24Smillert static	void	chkshot(BULLET *, BULLET *);
46c72b5b24Smillert static	void	chkslime(BULLET *, BULLET *);
47c72b5b24Smillert static	void	explshot(BULLET *, int, int);
48c72b5b24Smillert static	void	find_under(BULLET *, BULLET *);
49c72b5b24Smillert static	int	iswall(int, int);
50c72b5b24Smillert static	void	mark_boot(BULLET *);
51c72b5b24Smillert static	void	mark_player(BULLET *);
52c72b5b24Smillert static	int	move_drone(BULLET *);
53c72b5b24Smillert static	void	move_flyer(PLAYER *);
54c72b5b24Smillert static	int	move_normal_shot(BULLET *);
55c72b5b24Smillert static	void	move_slime(BULLET *, int, BULLET *);
56c72b5b24Smillert static	void	save_bullet(BULLET *);
57c72b5b24Smillert static	void	zapshot(BULLET *, BULLET *);
583faf6791Sd 
59c6d62522Sd /* Return true if there is pending activity */
60c6d62522Sd int
can_moveshots(void)610f16a76cSmestre can_moveshots(void)
62c6d62522Sd {
63c6d62522Sd 	PLAYER *pp;
64c6d62522Sd 
65c6d62522Sd 	/* Bullets are moving? */
66c6d62522Sd 	if (Bullets)
67c6d62522Sd 		return 1;
68c6d62522Sd 
69c6d62522Sd 	/* Explosions are happening? */
70c6d62522Sd 	if (can_rollexpl())
71c6d62522Sd 		return 1;
72c6d62522Sd 
73c6d62522Sd 	/* Things are flying? */
74c6d62522Sd 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
75c6d62522Sd 		if (pp->p_flying >= 0)
76c6d62522Sd 			return 1;
77c6d62522Sd 	for (pp = Player; pp < End_player; pp++)
78c6d62522Sd 		if (pp->p_flying >= 0)
79c6d62522Sd 			return 1;
80c6d62522Sd 
81c6d62522Sd 	/* Everything is quiet: */
82c6d62522Sd 	return 0;
83c6d62522Sd }
84c6d62522Sd 
853faf6791Sd /*
863faf6791Sd  * moveshots:
873faf6791Sd  *	Move the shots already in the air, taking explosions into account
883faf6791Sd  */
893faf6791Sd void
moveshots(void)900f16a76cSmestre moveshots(void)
913faf6791Sd {
923faf6791Sd 	BULLET	*bp, *next;
933faf6791Sd 	PLAYER	*pp;
943faf6791Sd 	int	x, y;
953faf6791Sd 	BULLET	*blist;
963faf6791Sd 
973faf6791Sd 	rollexpl();
983faf6791Sd 	if (Bullets == NULL)
99fab1dce0Sd 		goto no_bullets;
1003faf6791Sd 
1013faf6791Sd 	/*
102fab1dce0Sd 	 * First we move through the bullet list conf_bulspd times, looking
1033faf6791Sd 	 * for things we may have run into.  If we do run into
1043faf6791Sd 	 * something, we set up the explosion and disappear, checking
1053faf6791Sd 	 * for damage to any player who got in the way.
1063faf6791Sd 	 */
1073faf6791Sd 
108fab1dce0Sd 	/* Move the list to a working list */
1093faf6791Sd 	blist = Bullets;
1103faf6791Sd 	Bullets = NULL;
111fab1dce0Sd 
112fab1dce0Sd 	/* Work with bullets on the working list (blist) */
1133faf6791Sd 	for (bp = blist; bp != NULL; bp = next) {
1143faf6791Sd 		next = bp->b_next;
115fab1dce0Sd 
1163faf6791Sd 		x = bp->b_x;
1173faf6791Sd 		y = bp->b_y;
1183faf6791Sd 
119fab1dce0Sd 		/* Un-draw the bullet on all screens: */
120fab1dce0Sd 		Maze[y][x] = bp->b_over;
121fab1dce0Sd 		check(ALL_PLAYERS, y, x);
122fab1dce0Sd 
123fab1dce0Sd 		/* Decide how to move the bullet: */
1243faf6791Sd 		switch (bp->b_type) {
125fab1dce0Sd 
126fab1dce0Sd 		  /* Normal, atomic bullets: */
1273faf6791Sd 		  case SHOT:
1283faf6791Sd 		  case GRENADE:
1293faf6791Sd 		  case SATCHEL:
1303faf6791Sd 		  case BOMB:
1313faf6791Sd 			if (move_normal_shot(bp)) {
132fab1dce0Sd 				/* Still there: put back on the active list */
1333faf6791Sd 				bp->b_next = Bullets;
1343faf6791Sd 				Bullets = bp;
1353faf6791Sd 			}
1363faf6791Sd 			break;
137fab1dce0Sd 
138fab1dce0Sd 		  /* Slime bullets that explode into slime on impact: */
1393faf6791Sd 		  case SLIME:
1403faf6791Sd 			if (bp->b_expl || move_normal_shot(bp)) {
141fab1dce0Sd 				/* Still there: put back on the active list */
1423faf6791Sd 				bp->b_next = Bullets;
1433faf6791Sd 				Bullets = bp;
1443faf6791Sd 			}
1453faf6791Sd 			break;
146fab1dce0Sd 
147fab1dce0Sd 		  /* Drones that wander about: */
1483faf6791Sd 		  case DSHOT:
1493faf6791Sd 			if (move_drone(bp)) {
150fab1dce0Sd 				/* Still there: put back on the active list */
1513faf6791Sd 				bp->b_next = Bullets;
1523faf6791Sd 				Bullets = bp;
1533faf6791Sd 			}
1543faf6791Sd 			break;
155fab1dce0Sd 
156fab1dce0Sd 		  /* Other/unknown: */
1573faf6791Sd 		  default:
158fab1dce0Sd 			/* Place it back on the active list: */
1593faf6791Sd 			bp->b_next = Bullets;
1603faf6791Sd 			Bullets = bp;
1613faf6791Sd 			break;
1623faf6791Sd 		}
1633faf6791Sd 	}
1643faf6791Sd 
165fab1dce0Sd 	/* Again, hang the Bullets list off `blist' and work with that: */
1663faf6791Sd 	blist = Bullets;
1673faf6791Sd 	Bullets = NULL;
1683faf6791Sd 	for (bp = blist; bp != NULL; bp = next) {
1693faf6791Sd 		next = bp->b_next;
170fab1dce0Sd 		/* Is the bullet exploding? */
1713faf6791Sd 		if (!bp->b_expl) {
172fab1dce0Sd 			/*
173fab1dce0Sd 			 * Its still flying through the air.
174fab1dce0Sd 			 * Put it back on the bullet list.
175fab1dce0Sd 			 */
1763faf6791Sd 			save_bullet(bp);
177fab1dce0Sd 
178fab1dce0Sd 			/* All the monitors can see the bullet: */
1793faf6791Sd 			for (pp = Monitor; pp < End_monitor; pp++)
1803faf6791Sd 				check(pp, bp->b_y, bp->b_x);
181fab1dce0Sd 
182fab1dce0Sd 			/* All the scanning players can see the drone: */
1833faf6791Sd 			if (bp->b_type == DSHOT)
1843faf6791Sd 				for (pp = Player; pp < End_player; pp++)
1853faf6791Sd 					if (pp->p_scan >= 0)
1863faf6791Sd 						check(pp, bp->b_y, bp->b_x);
187fab1dce0Sd 		} else {
188fab1dce0Sd 			/* It is exploding. Check what we hit: */
1893faf6791Sd 			chkshot(bp, next);
190fab1dce0Sd 			/* Release storage for the destroyed bullet: */
191fab1dce0Sd 			free(bp);
192fab1dce0Sd 		}
1933faf6791Sd 	}
1943faf6791Sd 
195fab1dce0Sd 	/* Re-draw all the players: (in case a bullet wiped them out) */
1963faf6791Sd 	for (pp = Player; pp < End_player; pp++)
1973faf6791Sd 		Maze[pp->p_y][pp->p_x] = pp->p_face;
1983faf6791Sd 
199fab1dce0Sd no_bullets:
200fab1dce0Sd 
201fab1dce0Sd 	/* Move flying boots through the air: */
2023faf6791Sd 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
2033faf6791Sd 		if (pp->p_flying >= 0)
2043faf6791Sd 			move_flyer(pp);
205fab1dce0Sd 
206fab1dce0Sd 	/* Move flying players through the air: */
2073faf6791Sd 	for (pp = Player; pp < End_player; pp++) {
2083faf6791Sd 		if (pp->p_flying >= 0)
2093faf6791Sd 			move_flyer(pp);
210fab1dce0Sd 		/* Flush out the explosions: */
211fab1dce0Sd 		sendcom(pp, REFRESH);
2123faf6791Sd 		look(pp);
2133faf6791Sd 	}
2143faf6791Sd 
215fab1dce0Sd 	/* Flush out and synchronise all the displays: */
216fab1dce0Sd 	sendcom(ALL_PLAYERS, REFRESH);
2173faf6791Sd }
2183faf6791Sd 
2193faf6791Sd /*
2203faf6791Sd  * move_normal_shot:
221fab1dce0Sd  *	Move a normal shot along its trajectory.
222fab1dce0Sd  *	Returns false if the bullet no longer needs tracking.
2233faf6791Sd  */
2243faf6791Sd static int
move_normal_shot(BULLET * bp)2250f16a76cSmestre move_normal_shot(BULLET  *bp)
2263faf6791Sd {
2273faf6791Sd 	int	i, x, y;
2283faf6791Sd 	PLAYER	*pp;
2293faf6791Sd 
230fab1dce0Sd 	/*
231fab1dce0Sd 	 * Walk an unexploded bullet along conf_bulspd times, moving it
232fab1dce0Sd 	 * one unit along each step. We flag it as exploding if it
233fab1dce0Sd 	 * meets something.
234fab1dce0Sd 	 */
235fab1dce0Sd 
236fab1dce0Sd 	for (i = 0; i < conf_bulspd; i++) {
237fab1dce0Sd 
238fab1dce0Sd 		/* Stop if the bullet has already exploded: */
2393faf6791Sd 		if (bp->b_expl)
2403faf6791Sd 			break;
2413faf6791Sd 
242fab1dce0Sd 		/* Adjust the bullet's co-ordinates: */
2433faf6791Sd 		x = bp->b_x;
2443faf6791Sd 		y = bp->b_y;
2453faf6791Sd 		switch (bp->b_face) {
2463faf6791Sd 		  case LEFTS:
2473faf6791Sd 			x--;
2483faf6791Sd 			break;
2493faf6791Sd 		  case RIGHT:
2503faf6791Sd 			x++;
2513faf6791Sd 			break;
2523faf6791Sd 		  case ABOVE:
2533faf6791Sd 			y--;
2543faf6791Sd 			break;
2553faf6791Sd 		  case BELOW:
2563faf6791Sd 			y++;
2573faf6791Sd 			break;
2583faf6791Sd 		}
2593faf6791Sd 
260fab1dce0Sd 
261fab1dce0Sd 		/* Look at what the bullet is colliding with : */
2623faf6791Sd 		switch (Maze[y][x]) {
263fab1dce0Sd 		  /* Gun shots have a chance of collision: */
2643faf6791Sd 		  case SHOT:
265fab1dce0Sd 			if (rand_num(100) < conf_pshot_coll) {
2663faf6791Sd 				zapshot(Bullets, bp);
2673faf6791Sd 				zapshot(bp->b_next, bp);
2683faf6791Sd 			}
2693faf6791Sd 			break;
270fab1dce0Sd 		  /* Grenades only have a chance of collision: */
2713faf6791Sd 		  case GRENADE:
272fab1dce0Sd 			if (rand_num(100) < conf_pgren_coll) {
2733faf6791Sd 				zapshot(Bullets, bp);
2743faf6791Sd 				zapshot(bp->b_next, bp);
2753faf6791Sd 			}
2763faf6791Sd 			break;
277fab1dce0Sd 		  /* Reflecting walls richochet the bullet: */
278fab1dce0Sd 		  case WALL4:
2793faf6791Sd 			switch (bp->b_face) {
2803faf6791Sd 			  case LEFTS:
2813faf6791Sd 				bp->b_face = BELOW;
2823faf6791Sd 				break;
2833faf6791Sd 			  case RIGHT:
2843faf6791Sd 				bp->b_face = ABOVE;
2853faf6791Sd 				break;
2863faf6791Sd 			  case ABOVE:
2873faf6791Sd 				bp->b_face = RIGHT;
2883faf6791Sd 				break;
2893faf6791Sd 			  case BELOW:
2903faf6791Sd 				bp->b_face = LEFTS;
2913faf6791Sd 				break;
2923faf6791Sd 			}
2933faf6791Sd 			Maze[y][x] = WALL5;
2943faf6791Sd 			for (pp = Monitor; pp < End_monitor; pp++)
2953faf6791Sd 				check(pp, y, x);
2963faf6791Sd 			break;
2973faf6791Sd 		  case WALL5:
2983faf6791Sd 			switch (bp->b_face) {
2993faf6791Sd 			  case LEFTS:
3003faf6791Sd 				bp->b_face = ABOVE;
3013faf6791Sd 				break;
3023faf6791Sd 			  case RIGHT:
3033faf6791Sd 				bp->b_face = BELOW;
3043faf6791Sd 				break;
3053faf6791Sd 			  case ABOVE:
3063faf6791Sd 				bp->b_face = LEFTS;
3073faf6791Sd 				break;
3083faf6791Sd 			  case BELOW:
3093faf6791Sd 				bp->b_face = RIGHT;
3103faf6791Sd 				break;
3113faf6791Sd 			}
3123faf6791Sd 			Maze[y][x] = WALL4;
3133faf6791Sd 			for (pp = Monitor; pp < End_monitor; pp++)
3143faf6791Sd 				check(pp, y, x);
3153faf6791Sd 			break;
316fab1dce0Sd 		  /* Dispersion doors randomly disperse bullets: */
3173faf6791Sd 		  case DOOR:
3183faf6791Sd 			switch (rand_num(4)) {
3193faf6791Sd 			  case 0:
3203faf6791Sd 				bp->b_face = ABOVE;
3213faf6791Sd 				break;
3223faf6791Sd 			  case 1:
3233faf6791Sd 				bp->b_face = BELOW;
3243faf6791Sd 				break;
3253faf6791Sd 			  case 2:
3263faf6791Sd 				bp->b_face = LEFTS;
3273faf6791Sd 				break;
3283faf6791Sd 			  case 3:
3293faf6791Sd 				bp->b_face = RIGHT;
3303faf6791Sd 				break;
3313faf6791Sd 			}
3323faf6791Sd 			break;
333fab1dce0Sd 		  /* Bullets zing past fliers: */
3343faf6791Sd 		  case FLYER:
3353faf6791Sd 			pp = play_at(y, x);
3363faf6791Sd 			message(pp, "Zing!");
3373faf6791Sd 			break;
338fab1dce0Sd 		  /* Bullets encountering a player: */
3393faf6791Sd 		  case LEFTS:
3403faf6791Sd 		  case RIGHT:
3413faf6791Sd 		  case BELOW:
3423faf6791Sd 		  case ABOVE:
3433faf6791Sd 			/*
344fab1dce0Sd 			 * Give the person a chance to catch a
345fab1dce0Sd 			 * grenade if s/he is facing it:
3463faf6791Sd 			 */
3473faf6791Sd 			pp = play_at(y, x);
3483faf6791Sd 			pp->p_ident->i_shot += bp->b_charge;
3493faf6791Sd 			if (opposite(bp->b_face, Maze[y][x])) {
350fab1dce0Sd 			    /* Give them a 10% chance: */
351fab1dce0Sd 			    if (rand_num(100) < conf_pgren_catch) {
352fab1dce0Sd 				/* They caught it! */
3533faf6791Sd 				if (bp->b_owner != NULL)
3543faf6791Sd 					message(bp->b_owner,
3553faf6791Sd 					    "Your charge was absorbed!");
356fab1dce0Sd 
357fab1dce0Sd 				/*
358fab1dce0Sd 				 * The target player stole from the bullet's
359fab1dce0Sd 				 * owner. Charge stolen statistics:
360fab1dce0Sd 				 */
3613faf6791Sd 				if (bp->b_score != NULL)
3623faf6791Sd 					bp->b_score->i_robbed += bp->b_charge;
363fab1dce0Sd 
364fab1dce0Sd 				/* They acquire more ammo: */
3653faf6791Sd 				pp->p_ammo += bp->b_charge;
366fab1dce0Sd 
367fab1dce0Sd 				/* Check if it would have destroyed them: */
368fab1dce0Sd 				if (pp->p_damage + bp->b_size * conf_mindam
3693faf6791Sd 				    > pp->p_damcap)
370fab1dce0Sd 					/* Lucky escape statistics: */
3713faf6791Sd 					pp->p_ident->i_saved++;
372fab1dce0Sd 
373fab1dce0Sd 				/* Tell them: */
3743faf6791Sd 				message(pp, "Absorbed charge (good shield!)");
375fab1dce0Sd 
376fab1dce0Sd 				/* Absorbtion statistics: */
3773faf6791Sd 				pp->p_ident->i_absorbed += bp->b_charge;
378fab1dce0Sd 
379fab1dce0Sd 				/* Deallocate storage: */
38034944f90Stedu 				free(bp);
381fab1dce0Sd 
382fab1dce0Sd 				/* Update ammo display: */
383fab1dce0Sd 				ammo_update(pp);
384fab1dce0Sd 
385fab1dce0Sd 				/* No need for caller to keep tracking it: */
3863faf6791Sd 				return FALSE;
3873faf6791Sd 			    }
388fab1dce0Sd 
389fab1dce0Sd 			    /* Bullets faced head-on (statistics): */
3903faf6791Sd 			    pp->p_ident->i_faced += bp->b_charge;
3913faf6791Sd 			}
392fab1dce0Sd 
3933faf6791Sd 			/*
3943faf6791Sd 			 * Small chance that the bullet just misses the
3953faf6791Sd 			 * person.  If so, the bullet just goes on its
396fab1dce0Sd 			 * merry way without exploding. (5% chance)
3973faf6791Sd 			 */
398fab1dce0Sd 			if (rand_num(100) < conf_pmiss) {
399fab1dce0Sd 				/* Ducked statistics: */
4003faf6791Sd 				pp->p_ident->i_ducked += bp->b_charge;
401fab1dce0Sd 
402fab1dce0Sd 				/* Check if it would have killed them: */
403fab1dce0Sd 				if (pp->p_damage + bp->b_size * conf_mindam
4043faf6791Sd 				    > pp->p_damcap)
405fab1dce0Sd 					/* Lucky escape statistics: */
4063faf6791Sd 					pp->p_ident->i_saved++;
407fab1dce0Sd 
408fab1dce0Sd 				/* Shooter missed statistics: */
4093faf6791Sd 				if (bp->b_score != NULL)
4103faf6791Sd 					bp->b_score->i_missed += bp->b_charge;
411fab1dce0Sd 
412fab1dce0Sd 				/* Tell target that they were missed: */
4133faf6791Sd 				message(pp, "Zing!");
414fab1dce0Sd 
415fab1dce0Sd 				/* Tell the bullet owner they missed: */
416fab1dce0Sd 				if (bp->b_owner != NULL)
4173faf6791Sd 				    message(bp->b_owner,
4183faf6791Sd 					((bp->b_score->i_missed & 0x7) == 0x7) ?
4193faf6791Sd 					"My!  What a bad shot you are!" :
4203faf6791Sd 					"Missed him");
421fab1dce0Sd 
422fab1dce0Sd 				/* Don't fall through */
4233faf6791Sd 				break;
424fab1dce0Sd 			} else {
425fab1dce0Sd 				/* The player is to be blown up: */
426fab1dce0Sd 				bp->b_expl = TRUE;
4273faf6791Sd 			}
428fab1dce0Sd 			break;
429fab1dce0Sd 		  /* Bullet hits a wall, and always explodes: */
4303faf6791Sd 		  case WALL1:
4313faf6791Sd 		  case WALL2:
4323faf6791Sd 		  case WALL3:
4333faf6791Sd 			bp->b_expl = TRUE;
4343faf6791Sd 			break;
4353faf6791Sd 		}
4363faf6791Sd 
437fab1dce0Sd 		/* Update the bullet's new position: */
4383faf6791Sd 		bp->b_x = x;
4393faf6791Sd 		bp->b_y = y;
4403faf6791Sd 	}
441fab1dce0Sd 
442fab1dce0Sd 	/* Caller should keep tracking the bullet: */
4433faf6791Sd 	return TRUE;
4443faf6791Sd }
4453faf6791Sd 
4463faf6791Sd /*
4473faf6791Sd  * move_drone:
4483faf6791Sd  *	Move the drone to the next square
449fab1dce0Sd  *	Returns FALSE if the drone need no longer be tracked.
4503faf6791Sd  */
451fab1dce0Sd static int
move_drone(BULLET * bp)4520f16a76cSmestre move_drone(BULLET *bp)
4533faf6791Sd {
4543faf6791Sd 	int	mask, count;
455fab1dce0Sd 	int	n, dir = -1;
4563faf6791Sd 	PLAYER	*pp;
4573faf6791Sd 
458fab1dce0Sd 	/* See if we can give someone a blast: */
45950aad205Spjanzen 	if (is_player(Maze[bp->b_y][bp->b_x - 1])) {
4603faf6791Sd 		dir = WEST;
4613faf6791Sd 		goto drone_move;
4623faf6791Sd 	}
46350aad205Spjanzen 	if (is_player(Maze[bp->b_y - 1][bp->b_x])) {
4643faf6791Sd 		dir = NORTH;
4653faf6791Sd 		goto drone_move;
4663faf6791Sd 	}
46750aad205Spjanzen 	if (is_player(Maze[bp->b_y + 1][bp->b_x])) {
4683faf6791Sd 		dir = SOUTH;
4693faf6791Sd 		goto drone_move;
4703faf6791Sd 	}
47150aad205Spjanzen 	if (is_player(Maze[bp->b_y][bp->b_x + 1])) {
4723faf6791Sd 		dir = EAST;
4733faf6791Sd 		goto drone_move;
4743faf6791Sd 	}
4753faf6791Sd 
476fab1dce0Sd 	/* Find out what directions are clear and move that way: */
4773faf6791Sd 	mask = count = 0;
4783faf6791Sd 	if (!iswall(bp->b_y, bp->b_x - 1))
4793faf6791Sd 		mask |= WEST, count++;
4803faf6791Sd 	if (!iswall(bp->b_y - 1, bp->b_x))
4813faf6791Sd 		mask |= NORTH, count++;
4823faf6791Sd 	if (!iswall(bp->b_y + 1, bp->b_x))
4833faf6791Sd 		mask |= SOUTH, count++;
4843faf6791Sd 	if (!iswall(bp->b_y, bp->b_x + 1))
4853faf6791Sd 		mask |= EAST, count++;
4863faf6791Sd 
487fab1dce0Sd 	/* All blocked up, just wait: */
4883faf6791Sd 	if (count == 0)
4893faf6791Sd 		return TRUE;
4903faf6791Sd 
491fab1dce0Sd 	/* Only one way to go: */
4923faf6791Sd 	if (count == 1) {
4933faf6791Sd 		dir = mask;
4943faf6791Sd 		goto drone_move;
4953faf6791Sd 	}
4963faf6791Sd 
497fab1dce0Sd 	/* Avoid backtracking, and remove the direction we came from: */
4983faf6791Sd 	switch (bp->b_face) {
4993faf6791Sd 	  case LEFTS:
5003faf6791Sd 		if (mask & EAST)
5013faf6791Sd 			mask &= ~EAST, count--;
5023faf6791Sd 		break;
5033faf6791Sd 	  case RIGHT:
5043faf6791Sd 		if (mask & WEST)
5053faf6791Sd 			mask &= ~WEST, count--;
5063faf6791Sd 		break;
5073faf6791Sd 	  case ABOVE:
5083faf6791Sd 		if (mask & SOUTH)
5093faf6791Sd 			mask &= ~SOUTH, count--;
5103faf6791Sd 		break;
5113faf6791Sd 	  case BELOW:
5123faf6791Sd 		if (mask & NORTH)
5133faf6791Sd 			mask &= ~NORTH, count--;
5143faf6791Sd 		break;
5153faf6791Sd 	}
5163faf6791Sd 
517fab1dce0Sd 	/* Pick one of the remaining directions: */
5183faf6791Sd 	n = rand_num(count);
5193faf6791Sd 	if (n >= 0 && mask & NORTH)
5203faf6791Sd 		dir = NORTH, n--;
5213faf6791Sd 	if (n >= 0 && mask & SOUTH)
5223faf6791Sd 		dir = SOUTH, n--;
5233faf6791Sd 	if (n >= 0 && mask & EAST)
5243faf6791Sd 		dir = EAST, n--;
5253faf6791Sd 	if (n >= 0 && mask & WEST)
5263faf6791Sd 		dir = WEST, n--;
5273faf6791Sd 
5283faf6791Sd drone_move:
529fab1dce0Sd 	/* Move the drone: */
5303faf6791Sd 	switch (dir) {
531fab1dce0Sd 	  case -1:
532fab1dce0Sd 		/* no move */
5333faf6791Sd 	  case WEST:
5343faf6791Sd 		bp->b_x--;
5353faf6791Sd 		bp->b_face = LEFTS;
5363faf6791Sd 		break;
5373faf6791Sd 	  case EAST:
5383faf6791Sd 		bp->b_x++;
5393faf6791Sd 		bp->b_face = RIGHT;
5403faf6791Sd 		break;
5413faf6791Sd 	  case NORTH:
5423faf6791Sd 		bp->b_y--;
5433faf6791Sd 		bp->b_face = ABOVE;
5443faf6791Sd 		break;
5453faf6791Sd 	  case SOUTH:
5463faf6791Sd 		bp->b_y++;
5473faf6791Sd 		bp->b_face = BELOW;
5483faf6791Sd 		break;
5493faf6791Sd 	}
550fab1dce0Sd 
551fab1dce0Sd 	/* Look at what the drone moved onto: */
5523faf6791Sd 	switch (Maze[bp->b_y][bp->b_x]) {
5533faf6791Sd 	  case LEFTS:
5543faf6791Sd 	  case RIGHT:
5553faf6791Sd 	  case BELOW:
5563faf6791Sd 	  case ABOVE:
5573faf6791Sd 		/*
558fab1dce0Sd 		 * Players have a 1% chance of absorbing a drone,
559fab1dce0Sd 		 * if they are facing it.
5603faf6791Sd 		 */
561fab1dce0Sd 		if (rand_num(100) < conf_pdroneabsorb && opposite(bp->b_face,
562fab1dce0Sd 		    Maze[bp->b_y][bp->b_x])) {
563fab1dce0Sd 
564fab1dce0Sd 			/* Feel the power: */
5653faf6791Sd 			pp = play_at(bp->b_y, bp->b_x);
5663faf6791Sd 			pp->p_ammo += bp->b_charge;
5673faf6791Sd 			message(pp, "**** Absorbed drone ****");
568fab1dce0Sd 
569fab1dce0Sd 			/* Release drone storage: */
57034944f90Stedu 			free(bp);
571fab1dce0Sd 
572fab1dce0Sd 			/* Update ammo: */
573fab1dce0Sd 			ammo_update(pp);
574fab1dce0Sd 
575fab1dce0Sd 			/* No need for caller to keep tracking drone: */
5763faf6791Sd 			return FALSE;
5773faf6791Sd 		}
578fab1dce0Sd 		/* Detonate the drone: */
5793faf6791Sd 		bp->b_expl = TRUE;
5803faf6791Sd 		break;
5813faf6791Sd 	}
582fab1dce0Sd 
583fab1dce0Sd 	/* Keep tracking the drone. */
5843faf6791Sd 	return TRUE;
5853faf6791Sd }
5863faf6791Sd 
5873faf6791Sd /*
5883faf6791Sd  * save_bullet:
589fab1dce0Sd  *	Put a bullet back onto the bullet list
5903faf6791Sd  */
5913faf6791Sd static void
save_bullet(BULLET * bp)5920f16a76cSmestre save_bullet(BULLET *bp)
5933faf6791Sd {
594fab1dce0Sd 
595fab1dce0Sd 	/* Save what the bullet will be flying over: */
5963faf6791Sd 	bp->b_over = Maze[bp->b_y][bp->b_x];
597fab1dce0Sd 
5983faf6791Sd 	switch (bp->b_over) {
599fab1dce0Sd 	  /* Bullets that can pass through each other: */
6003faf6791Sd 	  case SHOT:
6013faf6791Sd 	  case GRENADE:
6023faf6791Sd 	  case SATCHEL:
6033faf6791Sd 	  case BOMB:
6043faf6791Sd 	  case SLIME:
6053faf6791Sd 	  case LAVA:
6063faf6791Sd 	  case DSHOT:
6073faf6791Sd 		find_under(Bullets, bp);
6083faf6791Sd 		break;
6093faf6791Sd 	}
6103faf6791Sd 
6113faf6791Sd 	switch (bp->b_over) {
612fab1dce0Sd 	  /* A bullet hits a player: */
6133faf6791Sd 	  case LEFTS:
6143faf6791Sd 	  case RIGHT:
6153faf6791Sd 	  case ABOVE:
6163faf6791Sd 	  case BELOW:
6173faf6791Sd 	  case FLYER:
6183faf6791Sd 		mark_player(bp);
6193faf6791Sd 		break;
620fab1dce0Sd 
621fab1dce0Sd 	  /* A bullet passes a boot: */
6223faf6791Sd 	  case BOOT:
6233faf6791Sd 	  case BOOT_PAIR:
6243faf6791Sd 		mark_boot(bp);
625fab1dce0Sd 		/* FALLTHROUGH */
6263faf6791Sd 
627fab1dce0Sd 	  /* The bullet flies over everything else: */
6283faf6791Sd 	  default:
6293faf6791Sd 		Maze[bp->b_y][bp->b_x] = bp->b_type;
6303faf6791Sd 		break;
6313faf6791Sd 	}
6323faf6791Sd 
633fab1dce0Sd 	/* Insert the bullet into the Bullets list: */
6343faf6791Sd 	bp->b_next = Bullets;
6353faf6791Sd 	Bullets = bp;
6363faf6791Sd }
6373faf6791Sd 
6383faf6791Sd /*
6393faf6791Sd  * move_flyer:
6403faf6791Sd  *	Update the position of a player in flight
6413faf6791Sd  */
6423faf6791Sd static void
move_flyer(PLAYER * pp)6430f16a76cSmestre move_flyer(PLAYER *pp)
6443faf6791Sd {
6453faf6791Sd 	int	x, y;
6463faf6791Sd 
6473faf6791Sd 	if (pp->p_undershot) {
6483faf6791Sd 		fixshots(pp->p_y, pp->p_x, pp->p_over);
6493faf6791Sd 		pp->p_undershot = FALSE;
6503faf6791Sd 	}
651fab1dce0Sd 
652fab1dce0Sd 	/* Restore what the flier was flying over */
6533faf6791Sd 	Maze[pp->p_y][pp->p_x] = pp->p_over;
654fab1dce0Sd 
655fab1dce0Sd 	/* Fly: */
6563faf6791Sd 	x = pp->p_x + pp->p_flyx;
6573faf6791Sd 	y = pp->p_y + pp->p_flyy;
658fab1dce0Sd 
659fab1dce0Sd 	/* Bouncing off the edges of the maze: */
6603faf6791Sd 	if (x < 1) {
6613faf6791Sd 		x = 1 - x;
6623faf6791Sd 		pp->p_flyx = -pp->p_flyx;
6633faf6791Sd 	}
6643faf6791Sd 	else if (x > WIDTH - 2) {
6653faf6791Sd 		x = (WIDTH - 2) - (x - (WIDTH - 2));
6663faf6791Sd 		pp->p_flyx = -pp->p_flyx;
6673faf6791Sd 	}
6683faf6791Sd 	if (y < 1) {
6693faf6791Sd 		y = 1 - y;
6703faf6791Sd 		pp->p_flyy = -pp->p_flyy;
6713faf6791Sd 	}
6723faf6791Sd 	else if (y > HEIGHT - 2) {
6733faf6791Sd 		y = (HEIGHT - 2) - (y - (HEIGHT - 2));
6743faf6791Sd 		pp->p_flyy = -pp->p_flyy;
6753faf6791Sd 	}
676fab1dce0Sd 
677fab1dce0Sd 	/* Make sure we don't land on something we can't: */
6783faf6791Sd again:
6793faf6791Sd 	switch (Maze[y][x]) {
6803faf6791Sd 	  default:
681fab1dce0Sd 		/*
682fab1dce0Sd 		 * Flier is over something other than space, a wall
683fab1dce0Sd 		 * or a door. Randomly move (drift) the flier a little bit
684fab1dce0Sd 		 * and then try again:
685fab1dce0Sd 		 */
6863faf6791Sd 		switch (rand_num(4)) {
6873faf6791Sd 		  case 0:
6883faf6791Sd 			PLUS_DELTA(x, WIDTH - 2);
6893faf6791Sd 			break;
6903faf6791Sd 		  case 1:
6913faf6791Sd 			MINUS_DELTA(x, 1);
6923faf6791Sd 			break;
6933faf6791Sd 		  case 2:
6943faf6791Sd 			PLUS_DELTA(y, HEIGHT - 2);
6953faf6791Sd 			break;
6963faf6791Sd 		  case 3:
6973faf6791Sd 			MINUS_DELTA(y, 1);
6983faf6791Sd 			break;
6993faf6791Sd 		}
7003faf6791Sd 		goto again;
701fab1dce0Sd 	  /* Give a little boost when about to land on a wall or door: */
7023faf6791Sd 	  case WALL1:
7033faf6791Sd 	  case WALL2:
7043faf6791Sd 	  case WALL3:
7053faf6791Sd 	  case WALL4:
7063faf6791Sd 	  case WALL5:
7073faf6791Sd 	  case DOOR:
7083faf6791Sd 		if (pp->p_flying == 0)
7093faf6791Sd 			pp->p_flying++;
7103faf6791Sd 		break;
711fab1dce0Sd 	  /* Spaces are okay: */
7123faf6791Sd 	  case SPACE:
7133faf6791Sd 		break;
7143faf6791Sd 	}
715fab1dce0Sd 
716fab1dce0Sd 	/* Update flier's coordinates: */
7173faf6791Sd 	pp->p_y = y;
7183faf6791Sd 	pp->p_x = x;
719fab1dce0Sd 
720fab1dce0Sd 	/* Consume 'flying' time: */
7213faf6791Sd 	if (pp->p_flying-- == 0) {
722fab1dce0Sd 		/* Land: */
7233faf6791Sd 		if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
724fab1dce0Sd 			/* Land a player - they stustain a fall: */
7253faf6791Sd 			checkdam(pp, (PLAYER *) NULL, (IDENT *) NULL,
726fab1dce0Sd 				rand_num(pp->p_damage / conf_fall_frac), FALL);
7273faf6791Sd 			pp->p_face = rand_dir();
7283faf6791Sd 			showstat(pp);
729fab1dce0Sd 		} else {
730fab1dce0Sd 			/* Land boots: */
7313faf6791Sd 			if (Maze[y][x] == BOOT)
7323faf6791Sd 				pp->p_face = BOOT_PAIR;
7333faf6791Sd 			Maze[y][x] = SPACE;
7343faf6791Sd 		}
7353faf6791Sd 	}
736fab1dce0Sd 
737fab1dce0Sd 	/* Save under the flier: */
7383faf6791Sd 	pp->p_over = Maze[y][x];
739fab1dce0Sd 	/* Draw in the flier: */
7403faf6791Sd 	Maze[y][x] = pp->p_face;
7413faf6791Sd 	showexpl(y, x, pp->p_face);
7423faf6791Sd }
7433faf6791Sd 
7443faf6791Sd /*
7453faf6791Sd  * chkshot
7463faf6791Sd  *	Handle explosions
7473faf6791Sd  */
7483faf6791Sd static void
chkshot(BULLET * bp,BULLET * next)7490f16a76cSmestre chkshot(BULLET *bp, BULLET *next)
7503faf6791Sd {
7513faf6791Sd 	int	y, x;
7523faf6791Sd 	int	dy, dx, absdy;
7533faf6791Sd 	int	delta, damage;
7543faf6791Sd 	char	expl;
7553faf6791Sd 	PLAYER	*pp;
7563faf6791Sd 
7573faf6791Sd 	delta = 0;
7583faf6791Sd 	switch (bp->b_type) {
7593faf6791Sd 	  case SHOT:
7603faf6791Sd 	  case MINE:
7613faf6791Sd 	  case GRENADE:
7623faf6791Sd 	  case GMINE:
7633faf6791Sd 	  case SATCHEL:
7643faf6791Sd 	  case BOMB:
7653faf6791Sd 		delta = bp->b_size - 1;
7663faf6791Sd 		break;
7673faf6791Sd 	  case SLIME:
7683faf6791Sd 	  case LAVA:
7693faf6791Sd 		chkslime(bp, next);
7703faf6791Sd 		return;
7713faf6791Sd 	  case DSHOT:
7723faf6791Sd 		bp->b_type = SLIME;
7733faf6791Sd 		chkslime(bp, next);
7743faf6791Sd 		return;
7753faf6791Sd 	}
776fab1dce0Sd 
777fab1dce0Sd 	/* Draw the explosion square: */
7783faf6791Sd 	for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
7793faf6791Sd 		if (y < 0 || y >= HEIGHT)
7803faf6791Sd 			continue;
7813faf6791Sd 		dy = y - bp->b_y;
7823faf6791Sd 		absdy = (dy < 0) ? -dy : dy;
7833faf6791Sd 		for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
784fab1dce0Sd 			/* Draw a part of the explosion cloud: */
7853faf6791Sd 			if (x < 0 || x >= WIDTH)
7863faf6791Sd 				continue;
7873faf6791Sd 			dx = x - bp->b_x;
7883faf6791Sd 			if (dx == 0)
7893faf6791Sd 				expl = (dy == 0) ? '*' : '|';
7903faf6791Sd 			else if (dy == 0)
7913faf6791Sd 				expl = '-';
7923faf6791Sd 			else if (dx == dy)
7933faf6791Sd 				expl = '\\';
7943faf6791Sd 			else if (dx == -dy)
7953faf6791Sd 				expl = '/';
7963faf6791Sd 			else
7973faf6791Sd 				expl = '*';
7983faf6791Sd 			showexpl(y, x, expl);
799fab1dce0Sd 
800fab1dce0Sd 			/* Check what poor bastard was in the explosion: */
8013faf6791Sd 			switch (Maze[y][x]) {
8023faf6791Sd 			  case LEFTS:
8033faf6791Sd 			  case RIGHT:
8043faf6791Sd 			  case ABOVE:
8053faf6791Sd 			  case BELOW:
8063faf6791Sd 			  case FLYER:
8073faf6791Sd 				if (dx < 0)
8083faf6791Sd 					dx = -dx;
8093faf6791Sd 				if (absdy > dx)
8103faf6791Sd 					damage = bp->b_size - absdy;
8113faf6791Sd 				else
8123faf6791Sd 					damage = bp->b_size - dx;
813fab1dce0Sd 
814fab1dce0Sd 				/* Everybody hurts, sometimes. */
8153faf6791Sd 				pp = play_at(y, x);
8163faf6791Sd 				checkdam(pp, bp->b_owner, bp->b_score,
817fab1dce0Sd 					damage * conf_mindam, bp->b_type);
8183faf6791Sd 				break;
8193faf6791Sd 			  case GMINE:
8203faf6791Sd 			  case MINE:
821fab1dce0Sd 				/* Mines detonate in a chain reaction: */
8223faf6791Sd 				add_shot((Maze[y][x] == GMINE) ?
8233faf6791Sd 					GRENADE : SHOT,
8243faf6791Sd 					y, x, LEFTS,
8253faf6791Sd 					(Maze[y][x] == GMINE) ?
8263faf6791Sd 					GRENREQ : BULREQ,
8273faf6791Sd 					(PLAYER *) NULL, TRUE, SPACE);
8283faf6791Sd 				Maze[y][x] = SPACE;
8293faf6791Sd 				break;
8303faf6791Sd 			}
8313faf6791Sd 		}
8323faf6791Sd 	}
8333faf6791Sd }
8343faf6791Sd 
8353faf6791Sd /*
8363faf6791Sd  * chkslime:
8373faf6791Sd  *	handle slime shot exploding
8383faf6791Sd  */
8393faf6791Sd static void
chkslime(BULLET * bp,BULLET * next)8400f16a76cSmestre chkslime(BULLET *bp, BULLET *next)
8413faf6791Sd {
8423faf6791Sd 	BULLET	*nbp;
8433faf6791Sd 
8443faf6791Sd 	switch (Maze[bp->b_y][bp->b_x]) {
845fab1dce0Sd 	  /* Slime explodes on walls and doors: */
8463faf6791Sd 	  case WALL1:
8473faf6791Sd 	  case WALL2:
8483faf6791Sd 	  case WALL3:
8493faf6791Sd 	  case WALL4:
8503faf6791Sd 	  case WALL5:
8513faf6791Sd 	  case DOOR:
8523faf6791Sd 		switch (bp->b_face) {
8533faf6791Sd 		  case LEFTS:
8543faf6791Sd 			bp->b_x++;
8553faf6791Sd 			break;
8563faf6791Sd 		  case RIGHT:
8573faf6791Sd 			bp->b_x--;
8583faf6791Sd 			break;
8593faf6791Sd 		  case ABOVE:
8603faf6791Sd 			bp->b_y++;
8613faf6791Sd 			break;
8623faf6791Sd 		  case BELOW:
8633faf6791Sd 			bp->b_y--;
8643faf6791Sd 			break;
8653faf6791Sd 		}
8663faf6791Sd 		break;
8673faf6791Sd 	}
868fab1dce0Sd 
869fab1dce0Sd 	/* Duplicate the unit of slime: */
870ca161728Sderaadt 	nbp = malloc(sizeof (BULLET));
871fab1dce0Sd 	if (nbp == NULL) {
8723fc386a2Sespie 		logit(LOG_ERR, "malloc");
873fab1dce0Sd 		return;
874fab1dce0Sd 	}
8753faf6791Sd 	*nbp = *bp;
876fab1dce0Sd 
877fab1dce0Sd 	/* Move it around: */
878fab1dce0Sd 	move_slime(nbp, nbp->b_type == SLIME ? conf_slimespeed :
879fab1dce0Sd 	    conf_lavaspeed, next);
8803faf6791Sd }
8813faf6791Sd 
8823faf6791Sd /*
8833faf6791Sd  * move_slime:
8843faf6791Sd  *	move the given slime shot speed times and add it back if
8853faf6791Sd  *	it hasn't fizzled yet
8863faf6791Sd  */
887fab1dce0Sd static void
move_slime(BULLET * bp,int speed,BULLET * next)8880f16a76cSmestre move_slime(BULLET *bp, int speed, BULLET *next)
8893faf6791Sd {
8903faf6791Sd 	int	i, j, dirmask, count;
8913faf6791Sd 	PLAYER	*pp;
8923faf6791Sd 	BULLET	*nbp;
8933faf6791Sd 
8943faf6791Sd 	if (speed == 0) {
8953faf6791Sd 		if (bp->b_charge <= 0)
89634944f90Stedu 			free(bp);
8973faf6791Sd 		else
8983faf6791Sd 			save_bullet(bp);
8993faf6791Sd 		return;
9003faf6791Sd 	}
9013faf6791Sd 
902fab1dce0Sd 	/* Draw it: */
9033faf6791Sd 	showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
904fab1dce0Sd 
9053faf6791Sd 	switch (Maze[bp->b_y][bp->b_x]) {
906fab1dce0Sd 	  /* Someone got hit by slime or lava: */
9073faf6791Sd 	  case LEFTS:
9083faf6791Sd 	  case RIGHT:
9093faf6791Sd 	  case ABOVE:
9103faf6791Sd 	  case BELOW:
9113faf6791Sd 	  case FLYER:
9123faf6791Sd 		pp = play_at(bp->b_y, bp->b_x);
9133faf6791Sd 		message(pp, "You've been slimed.");
914fab1dce0Sd 		checkdam(pp, bp->b_owner, bp->b_score, conf_mindam, bp->b_type);
9153faf6791Sd 		break;
916fab1dce0Sd 	  /* Bullets detonate in slime and lava: */
9173faf6791Sd 	  case SHOT:
9183faf6791Sd 	  case GRENADE:
9193faf6791Sd 	  case SATCHEL:
9203faf6791Sd 	  case BOMB:
9213faf6791Sd 	  case DSHOT:
9223faf6791Sd 		explshot(next, bp->b_y, bp->b_x);
9233faf6791Sd 		explshot(Bullets, bp->b_y, bp->b_x);
9243faf6791Sd 		break;
9253faf6791Sd 	}
9263faf6791Sd 
927fab1dce0Sd 
928fab1dce0Sd 	/* Drain the slime/lava of some energy: */
9293faf6791Sd 	if (--bp->b_charge <= 0) {
930fab1dce0Sd 		/* It fizzled: */
931fab1dce0Sd 		free(bp);
9323faf6791Sd 		return;
9333faf6791Sd 	}
9343faf6791Sd 
935fab1dce0Sd 	/* Figure out which way the slime should flow: */
9363faf6791Sd 	dirmask = 0;
9373faf6791Sd 	count = 0;
9383faf6791Sd 	switch (bp->b_face) {
9393faf6791Sd 	  case LEFTS:
9403faf6791Sd 		if (!iswall(bp->b_y, bp->b_x - 1))
9413faf6791Sd 			dirmask |= WEST, count++;
9423faf6791Sd 		if (!iswall(bp->b_y - 1, bp->b_x))
9433faf6791Sd 			dirmask |= NORTH, count++;
9443faf6791Sd 		if (!iswall(bp->b_y + 1, bp->b_x))
9453faf6791Sd 			dirmask |= SOUTH, count++;
9463faf6791Sd 		if (dirmask == 0)
9473faf6791Sd 			if (!iswall(bp->b_y, bp->b_x + 1))
9483faf6791Sd 				dirmask |= EAST, count++;
9493faf6791Sd 		break;
9503faf6791Sd 	  case RIGHT:
9513faf6791Sd 		if (!iswall(bp->b_y, bp->b_x + 1))
9523faf6791Sd 			dirmask |= EAST, count++;
9533faf6791Sd 		if (!iswall(bp->b_y - 1, bp->b_x))
9543faf6791Sd 			dirmask |= NORTH, count++;
9553faf6791Sd 		if (!iswall(bp->b_y + 1, bp->b_x))
9563faf6791Sd 			dirmask |= SOUTH, count++;
9573faf6791Sd 		if (dirmask == 0)
9583faf6791Sd 			if (!iswall(bp->b_y, bp->b_x - 1))
9593faf6791Sd 				dirmask |= WEST, count++;
9603faf6791Sd 		break;
9613faf6791Sd 	  case ABOVE:
9623faf6791Sd 		if (!iswall(bp->b_y - 1, bp->b_x))
9633faf6791Sd 			dirmask |= NORTH, count++;
9643faf6791Sd 		if (!iswall(bp->b_y, bp->b_x - 1))
9653faf6791Sd 			dirmask |= WEST, count++;
9663faf6791Sd 		if (!iswall(bp->b_y, bp->b_x + 1))
9673faf6791Sd 			dirmask |= EAST, count++;
9683faf6791Sd 		if (dirmask == 0)
9693faf6791Sd 			if (!iswall(bp->b_y + 1, bp->b_x))
9703faf6791Sd 				dirmask |= SOUTH, count++;
9713faf6791Sd 		break;
9723faf6791Sd 	  case BELOW:
9733faf6791Sd 		if (!iswall(bp->b_y + 1, bp->b_x))
9743faf6791Sd 			dirmask |= SOUTH, count++;
9753faf6791Sd 		if (!iswall(bp->b_y, bp->b_x - 1))
9763faf6791Sd 			dirmask |= WEST, count++;
9773faf6791Sd 		if (!iswall(bp->b_y, bp->b_x + 1))
9783faf6791Sd 			dirmask |= EAST, count++;
9793faf6791Sd 		if (dirmask == 0)
9803faf6791Sd 			if (!iswall(bp->b_y - 1, bp->b_x))
9813faf6791Sd 				dirmask |= NORTH, count++;
9823faf6791Sd 		break;
9833faf6791Sd 	}
9843faf6791Sd 	if (count == 0) {
9853faf6791Sd 		/*
9863faf6791Sd 		 * No place to go.  Just sit here for a while and wait
9873faf6791Sd 		 * for adjacent squares to clear out.
9883faf6791Sd 		 */
9893faf6791Sd 		save_bullet(bp);
9903faf6791Sd 		return;
9913faf6791Sd 	}
9923faf6791Sd 	if (bp->b_charge < count) {
9933faf6791Sd 		/* Only bp->b_charge paths may be taken */
9943faf6791Sd 		while (count > bp->b_charge) {
9953faf6791Sd 			if (dirmask & WEST)
9963faf6791Sd 				dirmask &= ~WEST;
9973faf6791Sd 			else if (dirmask & EAST)
9983faf6791Sd 				dirmask &= ~EAST;
9993faf6791Sd 			else if (dirmask & NORTH)
10003faf6791Sd 				dirmask &= ~NORTH;
10013faf6791Sd 			else if (dirmask & SOUTH)
10023faf6791Sd 				dirmask &= ~SOUTH;
10033faf6791Sd 			count--;
10043faf6791Sd 		}
10053faf6791Sd 	}
10063faf6791Sd 
1007fab1dce0Sd 	/* Spawn little slimes off in every possible direction: */
10083faf6791Sd 	i = bp->b_charge / count;
10093faf6791Sd 	j = bp->b_charge % count;
10103faf6791Sd 	if (dirmask & WEST) {
10113faf6791Sd 		count--;
10123faf6791Sd 		nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
10133faf6791Sd 			i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE);
10143faf6791Sd 		move_slime(nbp, speed - 1, next);
10153faf6791Sd 	}
10163faf6791Sd 	if (dirmask & EAST) {
10173faf6791Sd 		count--;
10183faf6791Sd 		nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
10193faf6791Sd 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
10203faf6791Sd 			bp->b_score, TRUE, SPACE);
10213faf6791Sd 		move_slime(nbp, speed - 1, next);
10223faf6791Sd 	}
10233faf6791Sd 	if (dirmask & NORTH) {
10243faf6791Sd 		count--;
10253faf6791Sd 		nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
10263faf6791Sd 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
10273faf6791Sd 			bp->b_score, TRUE, SPACE);
10283faf6791Sd 		move_slime(nbp, speed - 1, next);
10293faf6791Sd 	}
10303faf6791Sd 	if (dirmask & SOUTH) {
10313faf6791Sd 		count--;
10323faf6791Sd 		nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
10333faf6791Sd 			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
10343faf6791Sd 			bp->b_score, TRUE, SPACE);
10353faf6791Sd 		move_slime(nbp, speed - 1, next);
10363faf6791Sd 	}
10373faf6791Sd 
103834944f90Stedu 	free(bp);
10393faf6791Sd }
10403faf6791Sd 
10413faf6791Sd /*
10423faf6791Sd  * iswall:
10433faf6791Sd  *	returns whether the given location is a wall
10443faf6791Sd  */
10453faf6791Sd static int
iswall(int y,int x)10460f16a76cSmestre iswall(int y, int x)
10473faf6791Sd {
10483faf6791Sd 	if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
10493faf6791Sd 		return TRUE;
10503faf6791Sd 	switch (Maze[y][x]) {
10513faf6791Sd 	  case WALL1:
10523faf6791Sd 	  case WALL2:
10533faf6791Sd 	  case WALL3:
10543faf6791Sd 	  case WALL4:
10553faf6791Sd 	  case WALL5:
10563faf6791Sd 	  case DOOR:
10573faf6791Sd 	  case SLIME:
10583faf6791Sd 	  case LAVA:
10593faf6791Sd 		return TRUE;
10603faf6791Sd 	}
10613faf6791Sd 	return FALSE;
10623faf6791Sd }
10633faf6791Sd 
10643faf6791Sd /*
10653faf6791Sd  * zapshot:
10663faf6791Sd  *	Take a shot out of the air.
10673faf6791Sd  */
10683faf6791Sd static void
zapshot(BULLET * blist,BULLET * obp)10690f16a76cSmestre zapshot(BULLET *blist, BULLET *obp)
10703faf6791Sd {
10713faf6791Sd 	BULLET	*bp;
10723faf6791Sd 
10733faf6791Sd 	for (bp = blist; bp != NULL; bp = bp->b_next) {
1074fab1dce0Sd 		/* Find co-located bullets not facing the same way: */
1075fab1dce0Sd 		if (bp->b_face != obp->b_face
1076fab1dce0Sd 		    && bp->b_x == obp->b_x && bp->b_y == obp->b_y)
1077fab1dce0Sd 		{
1078fab1dce0Sd 			/* Bullet collision: */
10793faf6791Sd 			explshot(blist, obp->b_y, obp->b_x);
1080fab1dce0Sd 			return;
1081fab1dce0Sd 		}
1082fab1dce0Sd 	}
10833faf6791Sd }
10843faf6791Sd 
10853faf6791Sd /*
10863faf6791Sd  * explshot -
10873faf6791Sd  *	Make all shots at this location blow up
10883faf6791Sd  */
1089fab1dce0Sd static void
explshot(BULLET * blist,int y,int x)10900f16a76cSmestre explshot(BULLET *blist, int y, int x)
10913faf6791Sd {
10923faf6791Sd 	BULLET	*bp;
10933faf6791Sd 
10943faf6791Sd 	for (bp = blist; bp != NULL; bp = bp->b_next)
10953faf6791Sd 		if (bp->b_x == x && bp->b_y == y) {
10963faf6791Sd 			bp->b_expl = TRUE;
10973faf6791Sd 			if (bp->b_owner != NULL)
1098fab1dce0Sd 				message(bp->b_owner, "Shot intercepted.");
10993faf6791Sd 		}
11003faf6791Sd }
11013faf6791Sd 
11023faf6791Sd /*
11033faf6791Sd  * play_at:
11043faf6791Sd  *	Return a pointer to the player at the given location
11053faf6791Sd  */
11063faf6791Sd PLAYER *
play_at(int y,int x)11070f16a76cSmestre play_at(int y, int x)
11083faf6791Sd {
11093faf6791Sd 	PLAYER	*pp;
11103faf6791Sd 
11113faf6791Sd 	for (pp = Player; pp < End_player; pp++)
11123faf6791Sd 		if (pp->p_x == x && pp->p_y == y)
11133faf6791Sd 			return pp;
1114fab1dce0Sd 
1115fab1dce0Sd 	/* Internal fault: */
1116c6d62522Sd 	logx(LOG_ERR, "play_at: not a player");
1117fab1dce0Sd 	abort();
11183faf6791Sd }
11193faf6791Sd 
11203faf6791Sd /*
11213faf6791Sd  * opposite:
11223faf6791Sd  *	Return TRUE if the bullet direction faces the opposite direction
11233faf6791Sd  *	of the player in the maze
11243faf6791Sd  */
11253faf6791Sd int
opposite(int face,char dir)11260f16a76cSmestre opposite(int face, char dir)
11273faf6791Sd {
11283faf6791Sd 	switch (face) {
11293faf6791Sd 	  case LEFTS:
11303faf6791Sd 		return (dir == RIGHT);
11313faf6791Sd 	  case RIGHT:
11323faf6791Sd 		return (dir == LEFTS);
11333faf6791Sd 	  case ABOVE:
11343faf6791Sd 		return (dir == BELOW);
11353faf6791Sd 	  case BELOW:
11363faf6791Sd 		return (dir == ABOVE);
11373faf6791Sd 	  default:
11383faf6791Sd 		return FALSE;
11393faf6791Sd 	}
11403faf6791Sd }
11413faf6791Sd 
11423faf6791Sd /*
11433faf6791Sd  * is_bullet:
11443faf6791Sd  *	Is there a bullet at the given coordinates?  If so, return
11453faf6791Sd  *	a pointer to the bullet, otherwise return NULL
11463faf6791Sd  */
11473faf6791Sd BULLET *
is_bullet(int y,int x)11480f16a76cSmestre is_bullet(int y, int x)
11493faf6791Sd {
11503faf6791Sd 	BULLET	*bp;
11513faf6791Sd 
11523faf6791Sd 	for (bp = Bullets; bp != NULL; bp = bp->b_next)
11533faf6791Sd 		if (bp->b_y == y && bp->b_x == x)
11543faf6791Sd 			return bp;
11553faf6791Sd 	return NULL;
11563faf6791Sd }
11573faf6791Sd 
11583faf6791Sd /*
11593faf6791Sd  * fixshots:
11603faf6791Sd  *	change the underlying character of the shots at a location
11613faf6791Sd  *	to the given character.
11623faf6791Sd  */
11633faf6791Sd void
fixshots(int y,int x,char over)11640f16a76cSmestre fixshots(int y, int x, char over)
11653faf6791Sd {
11663faf6791Sd 	BULLET	*bp;
11673faf6791Sd 
11683faf6791Sd 	for (bp = Bullets; bp != NULL; bp = bp->b_next)
11693faf6791Sd 		if (bp->b_y == y && bp->b_x == x)
11703faf6791Sd 			bp->b_over = over;
11713faf6791Sd }
11723faf6791Sd 
11733faf6791Sd /*
11743faf6791Sd  * find_under:
11753faf6791Sd  *	find the underlying character for a bullet when it lands
11763faf6791Sd  *	on another bullet.
11773faf6791Sd  */
11783faf6791Sd static void
find_under(BULLET * blist,BULLET * bp)11790f16a76cSmestre find_under(BULLET *blist, BULLET *bp)
11803faf6791Sd {
11813faf6791Sd 	BULLET	*nbp;
11823faf6791Sd 
11833faf6791Sd 	for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
11843faf6791Sd 		if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
11853faf6791Sd 			bp->b_over = nbp->b_over;
11863faf6791Sd 			break;
11873faf6791Sd 		}
11883faf6791Sd }
11893faf6791Sd 
11903faf6791Sd /*
11913faf6791Sd  * mark_player:
11923faf6791Sd  *	mark a player as under a shot
11933faf6791Sd  */
11943faf6791Sd static void
mark_player(BULLET * bp)11950f16a76cSmestre mark_player(BULLET *bp)
11963faf6791Sd {
11973faf6791Sd 	PLAYER	*pp;
11983faf6791Sd 
11993faf6791Sd 	for (pp = Player; pp < End_player; pp++)
12003faf6791Sd 		if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
12013faf6791Sd 			pp->p_undershot = TRUE;
12023faf6791Sd 			break;
12033faf6791Sd 		}
12043faf6791Sd }
12053faf6791Sd 
12063faf6791Sd /*
12073faf6791Sd  * mark_boot:
12083faf6791Sd  *	mark a boot as under a shot
12093faf6791Sd  */
12103faf6791Sd static void
mark_boot(BULLET * bp)12110f16a76cSmestre mark_boot(BULLET *bp)
12123faf6791Sd {
12133faf6791Sd 	PLAYER	*pp;
12143faf6791Sd 
12153faf6791Sd 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
12163faf6791Sd 		if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
12173faf6791Sd 			pp->p_undershot = TRUE;
12183faf6791Sd 			break;
12193faf6791Sd 		}
12203faf6791Sd }
1221