[comp.sources.games] v05i075: hunt2 - multi-player maze exploration

games@tekred.TEK.COM (10/19/88)

Submitted by: conrad@cgl.ucsf.edu
Comp.sources.games: Volume 5, Issue 75
Archive-name: hunt2/Part01

	[Here's the promised latest version of hunt, replacing the one that
	 was posted, then later canceled. I've compiled and run this on
	 a Sun-3/60 in both the standalone and nothing modes.   -br]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 4)."
# Contents:  README MANIFEST ctl_transact.c hunt.c shots.c
# Wrapped by billr@saab on Wed Oct 19 09:23:31 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(6865 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XWhat *is* hunt?
X
X	Hunt is a multi-player search-and-destroy game that takes place
X	in a maze.  The game may either be slow and strategic or fast
X	and tactical, depending on how familiar the players are with the
X	keyboard commands.
X
XDistribution Policy:
X
X	Hunt is part of the user-contributed software distributed by
X	Berkeley in 4BSD.  The sources are copyrighted by the authors
X	and the University of California.  You may redistribute freely
X	as long as the copyright notices are retained.
X
XWords of Warning:
X
X	hunt uses the socket mechanism of 4BSD Unix, so if you are on
X		System V (my sympathies), you're on your own.
X	hunt can be configured to use Unix-domain sockets, but that
X		code has not been tested in recent memory.  Also, since
X		4.2BSD Unix-domain sockets are buggy, running hunt on
X		4.2BSD with Unix-domain sockets will probably crash
X		your system.  If you want to experiment, feel free to
X		do so.  However, don't say I didn't warn you :-).
X	hunt uses a fair amount of CPU time, both in user time (for
X		computing interactions) and system time (for processing
X		terminal interrupts).  We found that a VAX 750 can
X		support about three users before the system is
X		noticeably impacted.  The number goes up to about 8 or
X		10 for a VAX 8650.  On a network of Sun 3/50's with the
X		server running on a 3/280, things work much more
X		smoothly as the computing load is distributed across
X		many machines.
X	hunt may be dangerous to your health.  "Arthritic pain" and
X		"lack of circulation" in fingers have been reported by
X		hunt abusers.  Hunt may also be addictive, and the
X		withdrawal symptoms are not pretty :-)
X
XInstallation:
X
X	1. Edit file "Makefile" and make sure the options selected are
X		reasonable.  There are four "make" variables that you
X		should check: GAME_PARAM, SYSCFLAGS, SYSLDFLAGS, and DEFS.
X		GAME_PARAM controls what features of the game will be
X		compiled in (e.g. reflecting walls).  The optional features
X		are listed in comments above where GAME_PARAM is defined.
X		If you want to try them, just add the ones you want to the 
X		GAME_PARAM definition.
X
X		DEFS is where most system configuration is described.
X		If your system is 4.3BSD, Sun, Ultrix, Convex, HPUX
X		v6.0.1, or SGI, you're in luck.  We provide the
X		appropriate definitions for these systems and you just
X		need to select one of them (e.g. if you have an Ultrix
X		system, just change the line
X			DEFS=	$(GAME_PARAM) $(DEFS_43)
X		to
X			DEFS=	$(GAME_PARAM) $(DEFS_ULTRIX)
X		).  If your system is *not* listed above, then you may
X		need to do some experiments.  All of the options are
X		documented in the Makefile, be brave.
X
X		SYSCFLAGS and SYSLDFLAGS are used for "unusual" systems
X		and you probably won't need to deal with it.  An
X		example of an unusual system is the Silicon Graphics
X		IRIS, which keeps the network socket code in a BSD
X		emulation library that is in -lbsd.  Edit these only if
X		you *know* your system is "different."
X
X	2. Edit file "Makefile" and look at the "install:" target.  By
X		default, files are installed in /usr/games,
X		/usr/games/lib, and /usr/man/man6, which are "standard"
X		locations for games.  If your system has a local games
X		directory, you'll need to change these.
X	3. Edit file "pathname.c" and make sure the file names and port
X		numbers are reasonable.  You can ignore the first set
X		of variables as they are used only for debugging
X		purposes.  The second set is used in the installed
X		version of hunt.  The important variables are "Driver"
X		(where the server is kept), "Test_port" (the Internet
X		UDP port number that new players should use to contact
X		the server), and "Stat_file" (where scoring statistics
X		and body counts are written).  The only tricky variable
X		here is "Test_port".  The default value is chosen so
X		that it is unlikely to conflict with other service port
X		numbers, but you can change it if you want to.
X	4. Type "make install", which will compile and install the
X		programs and manual pages.  Now you're almost ready to
X		go (see next section).  There may be some warnings during
X		compilation.  Ignore them.
X
XSetting up the network:
X
X	Hunt may be set up in one of three modes: standalone, inetd, or
X	nothing.  In "standalone" mode, there is always a hunt server
X	running on a server machine.  All players who enter the game
X	will be talking to this server.  This is the mode we use at
X	UCSF.  The cost is one entry in the process table on the server
X	machine.  In "inetd" mode, the server is started via inetd.
X	Again, only one machine should be set up to answer game
X	requests.  The cost is having to edit a few system files.  In
X	"nothing" mode, no server is running when there is no one
X	playing.  The first person to enter hunt will automatically
X	start up a server on his machine.  This, of course, gives him
X	an unfair advantage.  Also, there may be race conditions such
X	that players end up in different games.  The choice of which
X	mode to use depends on site configuration and politics.  We
X	recommend using "standalone" mode because it is simple to set
X	up and starts up rapidly.
X
X	-----
X
X	FOR STANDALONE MODE, put these lines in /etc/rc.local on the
X	server machine.  THERE SHOULD ONLY BE ONE SERVER MACHINE!
X
X	# start up the hunt daemon if present
X	if [ -f /usr/games/lib/huntd ]; then
X		/usr/games/lib/huntd -s & (echo -n ' huntd')	>/dev/console
X	fi
X
X	Also, you should start one up (on the off chance that you will
X	want to test this mess :-) by typing "/usr/games/lib/huntd -s".
X
X	-----
X
X	FOR INETD MODE, then things get more complicated.  You need to
X	edit both /etc/services and /etc/inetd.conf.  In /etc/services,
X	add the line
X
X	hunt		26740/udp
X
X	26740 corresponds to the default "Test_port".  If you changed
X	that variable, then you should put whatever value you used here
X	as well.  In /etc/inetd.conf, add the line
X
X	hunt	dgram	udp	wait	nobody	/usr/games/lib/huntd	huntd
X
X	This works for 4.3BSD.  I don't remember the configuration file
X	format for 4.2BSD inetd.
X
X	See the huntd.6 manual page for more details.
X
X	-----
X
X	FOR NOTHING MODE, do nothing.
X
XTesting:
X	Now you are ready to test the code.  Type "/usr/games/hunt" or
X	whatever you call the hunt executable.  You should be prompted
X	for your name and team.  Then you should get the display of a
X	maze.  At this point, you should read the manual page :-).
X
X======
X
XHunt is not officially supported by anyone anywhere (that I know of);
Xhowever, bug reports will be read and bug fixes/enhancements may be
Xsent out at irregular intervals.  Send no flames, just money.  Happy
Xhunting.
X
X					Conrad Huang
X					conrad@cgl.ucsf.edu
X					Greg Couch
X					gregc@cgl.ucsf.edu
X					October 17, 1988
X
XP.S.  The authors of the game want to emphasize that this version of hunt
Xwas started over eight years ago, and the programming style exhibited here
Xin no way reflects the current programming practices of the authors.
END_OF_FILE
if test 6865 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(889 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   3	
X README                     1	
X answer.c                   3	
X bsd.h                      4	
X connect.c                  3	
X ctl.c                      2	
X ctl_transact.c             1	
X draw.c                     3	
X driver.c                   2	
X execute.c                  2	
X expl.c                     3	
X extern.c                   3	
X faketalk.c                 3	
X get_names.c                3	
X hunt.6                     2	
X hunt.c                     1	
X hunt.h                     3	
X huntd.6                    3	
X makemaze.c                 3	
X pathname.c                 4	
X playit.c                   2	
X shots.c                    1	
X talk_ctl.h                 4	
X terminal.c                 4	
END_OF_FILE
if test 889 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'ctl_transact.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ctl_transact.c'\"
else
echo shar: Extracting \"'ctl_transact.c'\" \(2460 characters\)
sed "s/^X//" >'ctl_transact.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1983 Regents of the University of California.
X * All rights reserved.  The Berkeley software License Agreement
X * specifies the terms and conditions for redistribution.
X */
X
X#include "bsd.h"
X
X#if	defined(TALK_43) || defined(TALK_42)
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)ctl_transact.c	5.2 (Berkeley) 3/13/86";
X#endif not lint
X
X#include "talk_ctl.h"
X#include <sys/time.h>
X
X#define CTL_WAIT 2	/* time to wait for a response, in seconds */
X#define MAX_RETRY 5
X
X/*
X * SOCKDGRAM is unreliable, so we must repeat messages if we have
X * not recieved an acknowledgement within a reasonable amount
X * of time
X */
Xctl_transact(target, msg, type, rp)
X	struct in_addr target;
X	CTL_MSG msg;
X	int type;
X	CTL_RESPONSE *rp;
X{
X	int read_mask, ctl_mask, nready, cc, retries;
X	struct timeval wait;
X
X	msg.type = type;
X	daemon_addr.sin_addr = target;
X	daemon_addr.sin_port = daemon_port;
X	ctl_mask = 1 << ctl_sockt;
X
X	/*
X	 * Keep sending the message until a response of
X	 * the proper type is obtained.
X	 */
X	do {
X		wait.tv_sec = CTL_WAIT;
X		wait.tv_usec = 0;
X		/* resend message until a response is obtained */
X		for (retries = MAX_RETRY; retries > 0; retries -= 1) {
X			cc = sendto(ctl_sockt, (char *)&msg, sizeof (msg), 0,
X				&daemon_addr, sizeof (daemon_addr));
X			if (cc != sizeof (msg)) {
X				if (errno == EINTR)
X					continue;
X				p_error("Error on write to talk daemon");
X			}
X			read_mask = ctl_mask;
X			nready = select(32, &read_mask, 0, 0, &wait);
X			if (nready < 0) {
X				if (errno == EINTR)
X					continue;
X				p_error("Error waiting for daemon response");
X			}
X			if (nready != 0)
X				break;
X		}
X		if (retries <= 0)
X			break;
X		/*
X		 * Keep reading while there are queued messages 
X		 * (this is not necessary, it just saves extra
X		 * request/acknowledgements being sent)
X		 */
X		do {
X			cc = recv(ctl_sockt, (char *)rp, sizeof (*rp), 0);
X			if (cc < 0) {
X				if (errno == EINTR)
X					continue;
X				p_error("Error on read from talk daemon");
X			}
X			read_mask = ctl_mask;
X			/* an immediate poll */
X			timerclear(&wait);
X			nready = select(32, &read_mask, 0, 0, &wait);
X		} while (nready > 0 && (
X#ifdef	TALK_43
X		    rp->vers != TALK_VERSION ||
X#endif
X		    rp->type != type));
X	} while (
X#ifdef	TALK_43
X	    rp->vers != TALK_VERSION ||
X#endif
X	    rp->type != type);
X	rp->id_num = ntohl(rp->id_num);
X#ifdef	TALK_43
X	rp->addr.sa_family = ntohs(rp->addr.sa_family);
X# else
X	rp->addr.sin_family = ntohs(rp->addr.sin_family);
X# endif
X}
X#endif
END_OF_FILE
if test 2460 -ne `wc -c <'ctl_transact.c'`; then
    echo shar: \"'ctl_transact.c'\" unpacked with wrong size!
fi
# end of 'ctl_transact.c'
fi
if test -f 'hunt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hunt.c'\"
else
echo shar: Extracting \"'hunt.c'\" \(19289 characters\)
sed "s/^X//" >'hunt.c' <<'END_OF_FILE'
X/*
X *  Hunt
X *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
X *  San Francisco, California
X *
X *  Copyright (c) 1985 Regents of the University of California.
X *  All rights reserved.  The Berkeley software License Agreement
X *  specifies the terms and conditions for redistribution.
X */
X
X# include	<errno.h>
X# include	<curses.h>
X# include	"hunt.h"
X# include	<signal.h>
X# include	<ctype.h>
X# include	<sys/stat.h>
X# ifndef HPUX
X# include	<sys/time.h>
X# else
X# include	<time.h>
X# endif
X# ifdef TERMINFO
X# include	<term.h>
X
X# define	CM		cursor_address
X
X# else
X
X/*
X * Some old versions of curses don't have these defined
X */
X# ifndef cbreak
X# define	cbreak()	crmode()
X# endif
X# ifndef baudrate
X# define	baudrate()	(_tty.sg_ospeed)
X# endif
X# endif
X
X/*
X *	these numbers are contrived to allow 3 users on a VAX 11/750
X *	i.e. an spin loop of 10000 iterations in 30 milliseconds.
X */
X# define	LOOP_COUNT	10000
X# define	FUDGE_FACTOR	30
X
XFLAG	Last_player = FALSE;
X# ifdef MONITOR
XFLAG	Am_monitor = FALSE;
X# endif MONITOR
X
Xchar	Buf[BUFSIZ];
X
Xint	Socket;
X# ifdef INTERNET
Xchar	*Sock_host;
Xchar	*use_port;
XFLAG	Query_driver = FALSE;
Xchar	*Send_message = NULL;
X# endif INTERNET
X
XSOCKET	Daemon;
X# ifdef	INTERNET
X# define	DAEMON_SIZE	(sizeof Daemon)
X# else	INTERNET
X# define	DAEMON_SIZE	(sizeof Daemon - 1)
X# endif	INTERNET
X
Xchar	map_key[256];			/* what to map keys to */
XFLAG	no_beep;
X
Xstatic char	name[NAMELEN];
Xstatic char	team = ' ';
X
Xextern int	cur_row, cur_col, _putchar();
Xextern char	*tgoto();
X
X/*
X * main:
X *	Main program for local process
X */
Xmain(ac, av)
Xint	ac;
Xchar	**av;
X{
X	char		*term;
X	int		c;
X	extern int	errno;
X	extern int	Otto_mode;
X	extern int	optind;
X	extern char	*optarg;
X	long		enter_status;
X	int		intr(), sigterm(), sigemt(), tstp();
X	long		env_init(), quit();
X
X	enter_status = env_init((long) Q_CLOAK);
X	while ((c = getopt(ac, av, "bcfh:l:mn:op:qst:w:")) != EOF) {
X		switch (c) {
X
X		case 'l':	/* rsh compatibility */
X		case 'n':
X			(void) strncpy(name, optarg, NAMELEN);
X			break;
X		case 't':
X			team = *optarg;
X			if (!isdigit(team)) {
X				fprintf(stderr, "Team names must be numeric\n");
X				team = ' ';
X			}
X			break;
X		case 'o':
X# ifndef OTTO
X			fputs("The -o flag is reserved for future use.\n",
X				stderr);
X			goto usage;
X# else OTTO
X			Otto_mode = TRUE;
X			break;
X# endif OTTO
X# ifdef MONITOR
X		case 'm':
X			Am_monitor = TRUE;
X			break;
X# endif MONITOR
X# ifdef INTERNET
X		case 'q':	/* query whether hunt is running */
X			Query_driver = TRUE;
X			break;
X		case 'w':
X			Send_message = optarg;
X			break;
X		case 'h':
X			Sock_host = optarg;
X			break;
X		case 'p':
X			use_port = optarg;
X			Test_port = atoi(use_port);
X			break;
X# endif INTERNET
X		case 'c':
X			enter_status = Q_CLOAK;
X			break;
X# ifdef FLY
X		case 'f':
X			enter_status = Q_FLY;
X			break;
X# endif FLY
X		case 's':
X			enter_status = Q_SCAN;
X			break;
X		case 'b':
X			no_beep = !no_beep;
X			break;
X		default:
X		usage:
X# ifdef INTERNET
X#  ifdef MONITOR
X#   define	USAGE	"usage:\thunt [-qmcsf] [-n name] [-t team]\n\t[-p port] [-w message] [host]\n"
X#  else MONITOR
X#   define	USAGE	"usage:\thunt [-qcsf] [-n name] [-t team]\n\t[-p port] [-w message] [host]\n"
X#  endif MONITOR
X# else INTERNET
X#  ifdef MONITOR
X#   define	USAGE	"usage:\thunt [-mcsf] [-n name] [-t team]\n"
X#  else MONITOR
X#   define	USAGE	"usage:\thunt [-csf] [-n name] [-t team]\n"
X#  endif MONITOR
X# endif INTERNET
X			fputs(USAGE, stderr);
X# undef USAGE
X			exit(1);
X		}
X	}
X# ifdef INTERNET
X	if (optind + 1 < ac)
X		goto usage;
X	else if (optind + 1 == ac)
X		Sock_host = av[ac - 1];
X# else INTERNET
X	if (optind > ac)
X		goto usage;
X# endif INTERNET	
X
X# ifdef INTERNET
X	if (Query_driver) {
X		find_driver(FALSE);
X		if (Daemon.sin_port != 0) {
X			struct	hostent	*hp;
X			int	num_players;
X
X			hp = gethostbyaddr((char *) &Daemon.sin_addr,
X				sizeof Daemon.sin_addr, AF_INET);
X			num_players = ntohs(Daemon.sin_port);
X			printf("%d player%s hunting on %s!\n",
X				num_players, (num_players == 1) ? "" : "s",
X				hp != NULL ? hp->h_name :
X				inet_ntoa(Daemon.sin_addr));
X		}
X		exit(Daemon.sin_port);
X	}
X# endif INTERNET
X# ifdef OTTO
X	if (Otto_mode)
X		(void) strncpy(name, "otto", NAMELEN);
X	else
X# endif OTTO
X	fill_in_blanks();
X
X	(void) fflush(stdout);
X	if (!isatty(0) || (term = getenv("TERM")) == NULL) {
X		fprintf(stderr, "no terminal type\n");
X		exit(1);
X	}
X# ifdef TERMINFO
X	initscr();
X	noecho();
X	cbreak();
X# else
X	_tty_ch = 0;
X	gettmode();
X	setterm(term);
X	noecho();
X	cbreak();
X	_puts(TI);
X	_puts(VS);
X# endif
X	clear_the_screen();
X	(void) signal(SIGINT, intr);
X	(void) signal(SIGTERM, sigterm);
X	(void) signal(SIGEMT, sigemt);
X	(void) signal(SIGPIPE, SIG_IGN);
X#ifdef	SIGTSTP
X	(void) signal(SIGTSTP, tstp);
X#endif
X
X	for (;;) {
X		{
X			register int	loop;
X			struct	timeval	start, stop;
X			int		elapsed_time;
X
X			(void) gettimeofday(&start, (struct timezone *) NULL);
X			for (loop = 0; loop < LOOP_COUNT; loop++)
X				continue;
X			(void) gettimeofday(&stop, (struct timezone *) NULL);
X			elapsed_time = (stop.tv_sec - start.tv_sec) * 1000000
X						+ stop.tv_usec - start.tv_usec;
X			if (elapsed_time > LOOP_COUNT * FUDGE_FACTOR)
X				leave(1, "Response time too slow");
X		}
X
X# ifdef	INTERNET
X		find_driver(TRUE);
X
X		if (Daemon.sin_port == 0)
X			leave(1, "Game not found, try again");
X
X		do {
X			int	option;
X
X			Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
X			if (Socket < 0) {
X				perror("socket");
X				exit(1);
X			}
X			option = 1;
X			if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK,
X			    &option, sizeof option) < 0)
X				perror("setsockopt loopback");
X			errno = 0;
X			if (connect(Socket, (struct sockaddr *) &Daemon,
X			    DAEMON_SIZE) < 0) {
X				if (errno != ECONNREFUSED) {
X					perror("connect");
X					leave(1, "connect");
X				}
X			}
X			else
X				break;
X			sleep(1);
X		} while (close(Socket) == 0);
X# else	INTERNET
X		/*
X		 * set up a socket
X		 */
X
X		if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0) {
X			perror("socket");
X			exit(1);
X		}
X
X		/*
X		 * attempt to connect the socket to a name; if it fails that
X		 * usually means that the driver isn't running, so we start
X		 * up the driver.
X		 */
X
X		Daemon.sun_family = SOCK_FAMILY;
X		(void) strcpy(Daemon.sun_path, Sock_name);
X		if (connect(Socket, &Daemon, DAEMON_SIZE) < 0) {
X			if (errno != ENOENT) {
X				perror("connect");
X				leave(1, "connect2");
X			}
X			start_driver();
X
X			do {
X				(void) close(Socket);
X				if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0) {
X					perror("socket");
X					exit(1);
X				}
X				sleep(2);
X			} while (connect(Socket, &Daemon, DAEMON_SIZE) < 0);
X		}
X# endif INTERNET
X
X		do_connect(name, team, enter_status);
X# ifdef INTERNET
X		if (Send_message != NULL) {
X			do_message();
X			if (enter_status == Q_MESSAGE)
X				break;
X			Send_message = NULL;
X			continue;
X		}
X# endif
X		playit();
X		if ((enter_status = quit(enter_status)) == Q_QUIT)
X			break;
X	}
X	leave(0, (char *) NULL);
X	/* NOTREACHED */
X}
X
X# ifdef INTERNET
X# ifdef BROADCAST
Xbroadcast_vec(s, vector)
X	int			s;		/* socket */
X	struct	sockaddr	**vector;
X{
X	char			if_buf[BUFSIZ];
X	struct	ifconf		ifc;
X	struct	ifreq		*ifr;
X	unsigned int		n;
X	int			vec_cnt;
X
X	*vector = NULL;
X	ifc.ifc_len = sizeof if_buf;
X	ifc.ifc_buf = if_buf;
X	if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
X		return 0;
X	vec_cnt = 0;
X	n = ifc.ifc_len / sizeof (struct ifreq);
X	*vector = (struct sockaddr *) malloc(n * sizeof (struct sockaddr));
X	for (ifr = ifc.ifc_req; n != 0; n--, ifr++)
X		if (ioctl(s, SIOCGIFBRDADDR, ifr) >= 0)
X			bcopy(&ifr->ifr_addr, &(*vector)[vec_cnt++],
X						sizeof (struct sockaddr));
X	return vec_cnt;
X}
X# endif BROADCAST
X
Xfind_driver(do_startup)
XFLAG	do_startup;
X{
X	int			option;
X	short			msg;
X	short			port_num;
X	static SOCKET		test;
X	int			test_socket;
X	int			namelen;
X	char			local_name[256];
X	static			initial = TRUE;
X	static struct in_addr	local_address;
X	register struct hostent	*hp;
X	int			(*oldsigalrm)(), sigalrm();
X	extern int		errno;
X# ifdef BROADCAST
X	static	int		brdc;
X	static	SOCKET		*brdv;
X	int			i;
X# endif BROADCAST
X
X	if (Sock_host != NULL) {
X		if (!initial)
X			return;		/* Daemon address already valid */
X		initial = FALSE;
X		if ((hp = gethostbyname(Sock_host)) == NULL) {
X			leave(1, "Unknown host");
X			/* NOTREACHED */
X		}
X		Daemon.sin_family = SOCK_FAMILY;
X		Daemon.sin_addr = *((struct in_addr *) hp->h_addr);
X
X		test.sin_family = SOCK_FAMILY;
X		test.sin_addr = Daemon.sin_addr;
X		test.sin_port = htons(Test_port);
X	}
X
X
X	if (initial) {			/* do one time initialization */
X# ifndef BROADCAST
X		sethostent(1);		/* don't bother to close host file */
X# endif BROADCAST
X		if (gethostname(local_name, sizeof local_name) < 0) {
X			leave(1, "Sorry, I have no name.");
X			/* NOTREACHED */
X		}
X		if ((hp = gethostbyname(local_name)) == NULL) {
X			leave(1, "Can't find myself.");
X			/* NOTREACHED */
X		}
X		local_address = * ((struct in_addr *) hp->h_addr);
X
X		test.sin_family = SOCK_FAMILY;
X		test.sin_addr = local_address;
X		test.sin_port = htons(Test_port);
X	}
X
X	test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
X	if (test_socket < 0) {
X		perror("socket");
X		leave(1, "socket system call failed");
X		/* NOTREACHED */
X	}
X
X	if (Sock_host != NULL) {
Xtest_one_host:
X		test.sin_family = SOCK_FAMILY;
X		test.sin_addr = Daemon.sin_addr;
X		test.sin_port = htons(Test_port);
X
X		if (Am_monitor)
X			msg = htons(C_MONITOR);
X		else if (Query_driver)
X			msg = htons(C_MESSAGE);
X		else
X			msg = htons(C_PLAYER);
X		(void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
X		    (struct sockaddr *) &test, DAEMON_SIZE);
X		goto get_response;
X	}
X
X	if (!initial) {
X		/* favor host of previous session by broadcasting to it first */
X		test.sin_addr = Daemon.sin_addr;
X		test.sin_port = htons(Test_port);
X		msg = htons(0);	/* Must be playing! */
X		(void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
X		    (struct sockaddr *) &test, DAEMON_SIZE);
X	}
X
X
X# ifdef BROADCAST
X	if (initial)
X		brdc = broadcast_vec(test_socket, &brdv);
X
X	if (brdc <= 0) {
X		Daemon.sin_family = SOCK_FAMILY;
X		Daemon.sin_addr = local_address;
X		initial = FALSE;
X		goto test_one_host;
X	}
X
X# ifdef SO_BROADCAST
X	option = 1;
X	if (setsockopt(test_socket, SOL_SOCKET, SO_BROADCAST,
X	    (int) &option, sizeof option) < 0) {
X		perror("setsockopt broadcast");
X		leave(1, "setsockopt broadcast");
X		/* NOTREACHED */
X	}
X# endif
X
X	/* send broadcast packets on all interfaces */
X	if (Am_monitor)
X		msg = htons(C_MONITOR);
X	else if (Query_driver)
X		msg = htons(C_MESSAGE);
X	else
X		msg = htons(C_PLAYER);
X	for (i = 0; i < brdc; i++) {
X		bcopy(&brdv[i], &test, sizeof (SOCKET));
X		test.sin_port = htons(Test_port);
X		if (sendto(test_socket, (char *) &msg, sizeof msg, 0,
X		    (struct sockaddr *) &test, DAEMON_SIZE) < 0) {
X			perror("sendto");
X			leave(1, "sendto");
X			/* NOTREACHED */
X		}
X	}
X# else BROADCAST
X	/* loop thru all hosts on local net and send msg to them. */
X	if (Am_monitor)
X		msg = htons(C_MONITOR);
X	else if (Query_driver)
X		msg = htons(C_MESSAGE);
X	else
X		msg = htons(C_PLAYER);
X	sethostent(0);		/* rewind host file */
X	while (hp = gethostent()) {
X		if (inet_netof(test.sin_addr)
X		== inet_netof(* ((struct in_addr *) hp->h_addr))) {
X			test.sin_addr = * ((struct in_addr *) hp->h_addr);
X			(void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
X			    (struct sockaddr *) &test, DAEMON_SIZE);
X		}
X	}
X# endif BROADCAST
X
Xget_response:
X	namelen = DAEMON_SIZE;
X	oldsigalrm = signal(SIGALRM, sigalrm);
X	errno = 0;
X	(void) alarm(1);
X	if (recvfrom(test_socket, (char *) &port_num, sizeof port_num, 0,
X	    (struct sockaddr *) &Daemon, &namelen) < 0)
X	{
X		if (errno != EINTR) {
X			perror("recvfrom");
X			leave(1, "recvfrom");
X			/* NOTREACHED */
X		}
X		(void) alarm(0);
X		(void) signal(SIGALRM, oldsigalrm);
X		Daemon.sin_family = SOCK_FAMILY;
X		Daemon.sin_addr = local_address;
X		if (!do_startup)
X			Daemon.sin_port = 0;
X		else {
X			start_driver();
X			sleep(2);
X			find_driver(FALSE);		/* try again */
X		}
X	} else {
X		(void) alarm(0);
X		(void) signal(SIGALRM, oldsigalrm);
X		/*
X		 * Note that we do *not* convert from network to host
X		 * order since the port number *should* be in network order
X		 */
X		Daemon.sin_port = port_num;
X	}
X	(void) close(test_socket);
X	initial = FALSE;
X}
X# endif INTERNET
X
Xstart_driver()
X{
X	register int	procid;
X
X# ifdef MONITOR
X	if (Am_monitor) {
X		leave(1, "No one playing.");
X		/* NOTREACHED */
X	}
X# endif MONITOR
X
X# ifdef INTERNET
X	if (Sock_host != NULL) {
X		sleep(3);
X		return 0;
X	}
X# endif INTERNET
X
X	mvcur(cur_row, cur_col, 23, 0);
X	cur_row = 23;
X	cur_col = 0;
X	put_str("Starting...");
X	fflush(stdout);
X# ifndef BSD
X	procid = fork();
X# else
X	procid = vfork();
X# endif
X	if (procid == -1) {
X		perror("fork");
X		leave(1, "fork failed.");
X	}
X	if (procid == 0) {
X		(void) signal(SIGINT, SIG_IGN);
X		(void) close(Socket);
X		if (use_port == NULL)
X			execl(Driver, "HUNT", (char *) NULL);
X		else 
X			execl(Driver, "HUNT", "-p", use_port, (char *) NULL);
X		/* only get here if exec failed */
X		kill(getppid(), SIGEMT);	/* tell mom */
X		_exit(1);
X	}
X	mvcur(cur_row, cur_col, 23, 0);
X	cur_row = 23;
X	cur_col = 0;
X	put_str("Connecting...");
X	fflush(stdout);
X	return 0;
X}
X
X/*
X * bad_con:
X *	We had a bad connection.  For the moment we assume that this
X *	means the game is full.
X */
Xbad_con()
X{
X	leave(1, "The game is full.  Sorry.");
X	/* NOTREACHED */
X}
X
X/*
X * bad_ver:
X *	version number mismatch.
X */
Xbad_ver()
X{
X	leave(1, "Version number mismatch. No go.");
X	/* NOTREACHED */
X}
X
X/*
X * sigterm:
X *	Handle a terminate signal
X */
Xsigterm()
X{
X	leave(0, (char *) NULL);
X	/* NOTREACHED */
X}
X
X
X/*
X * sigemt:
X *	Handle a emt signal - shouldn't happen on vaxes(?)
X */
Xsigemt()
X{
X	leave(1, "Unable to start driver.  Try again.");
X	/* NOTREACHED */
X}
X
X# ifdef INTERNET
X/*
X * sigalrm:
X *	Handle an alarm signal
X */
Xsigalrm()
X{
X	return;
X}
X# endif INTERNET
X
X/*
X * rmnl:
X *	Remove a '\n' at the end of a string if there is one
X */
Xrmnl(s)
Xchar	*s;
X{
X	register char	*cp;
X	char		*rindex();
X
X	cp = rindex(s, '\n');
X	if (cp != NULL)
X		*cp = '\0';
X}
X
X/*
X * intr:
X *	Handle a interrupt signal
X */
Xintr()
X{
X	register int	ch;
X	register int	explained;
X	register int	y, x;
X
X	(void) signal(SIGINT, SIG_IGN);
X	y = cur_row;
X	x = cur_col;
X	mvcur(cur_row, cur_col, 23, 0);
X	cur_row = 23;
X	cur_col = 0;
X	put_str("Really quit? ");
X	clear_eol();
X	fflush(stdout);
X	explained = FALSE;
X	for (;;) {
X		ch = getchar();
X		if (isupper(ch))
X			ch = tolower(ch);
X		if (ch == 'y') {
X			(void) write(Socket, "q", 1);
X			(void) close(Socket);
X			leave(0, (char *) NULL);
X		}
X		else if (ch == 'n') {
X			(void) signal(SIGINT, intr);
X			mvcur(cur_row, cur_col, y, x);
X			cur_row = y;
X			cur_col = x;
X			fflush(stdout);
X			return;
X		}
X		if (!explained) {
X			put_str("(Yes or No) ");
X			fflush(stdout);
X			explained = TRUE;
X		}
X		(void) putchar(CTRL(G));
X		(void) fflush(stdout);
X	}
X}
X
X/*
X * leave:
X *	Leave the game somewhat gracefully, restoring all current
X *	tty stats.
X */
Xleave(eval, mesg)
Xint	eval;
Xchar	*mesg;
X{
X	if (baudrate() != 0) {
X		mvcur(cur_row, cur_col, 23, 0);
X		fflush(stdout);		/* flush in case VE changes pages */
X# ifdef TERMINFO
X		putp(cursor_normal);
X		putp(exit_ca_mode);
X		reset_shell_mode();
X# else
X		resetty();
X		_puts(VE);
X		_puts(TE);
X# endif
X	}
X	if (mesg != NULL)
X		puts(mesg);
X	exit(eval);
X}
X
X#ifdef	SIGTSTP
X/*
X * tstp:
X *	Handle stop and start signals
X */
Xtstp()
X{
X# ifndef TERMINFO
X	static struct sgttyb	tty;
X# endif
X	int	y, x;
X
X	y = cur_row;
X	x = cur_col;
X	mvcur(cur_row, cur_col, 23, 0);
X	cur_row = 23;
X	cur_col = 0;
X# ifdef TERMINFO
X	putp(cursor_normal);
X	putp(exit_ca_mode);
X	reset_shell_mode();
X# else
X	tty = _tty;
X	_puts(VE);
X	_puts(TE);
X	(void) fflush(stdout);
X	resetty();
X# endif
X	(void) kill(getpid(), SIGSTOP);
X	(void) signal(SIGTSTP, tstp);
X# ifdef TERMINFO
X	reset_prog_mode();
X	putp(enter_ca_mode);
X	putp(cursor_visible);
X# else
X	_tty = tty;
X	(void) stty(_tty_ch, &_tty);
X	_puts(TI);
X	_puts(VS);
X# endif
X	cur_row = y;
X	cur_col = x;
X# ifdef TERMINFO
X	putp(tgoto(CM, cur_row, cur_col));
X# else
X	_puts(tgoto(CM, cur_row, cur_col));
X# endif
X	redraw_screen();
X	fflush(stdout);
X}
X#endif
X
Xlong
Xenv_init(enter_status)
X	long	enter_status;
X{
X	register int	i;
X	char	*envp, *envname, *s, *index(), *strpbrk();
X
X	for (i = 0; i < 256; i++)
X		map_key[i] = (char) i;
X
X	envname = NULL;
X	if ((envp = getenv("HUNT")) != NULL) {
X		while ((s = strpbrk(envp, "=,")) != NULL) {
X			if (strncmp(envp, "cloak,", s - envp + 1) == 0) {
X				enter_status = Q_CLOAK;
X				envp = s + 1;
X			}
X			else if (strncmp(envp, "scan,", s - envp + 1) == 0) {
X				enter_status = Q_SCAN;
X				envp = s + 1;
X			}
X			else if (strncmp(envp, "fly,", s - envp + 1) == 0) {
X				enter_status = Q_FLY;
X				envp = s + 1;
X			}
X			else if (strncmp(envp, "nobeep,", s - envp + 1) == 0) {
X				no_beep = TRUE;
X				envp = s + 1;
X			}
X			else if (strncmp(envp, "name=", s - envp + 1) == 0) {
X				envname = s + 1;
X				if ((s = index(envp, ',')) == NULL) {
X					*envp = '\0';
X					strncpy(name, envname, NAMELEN);
X					break;
X				}
X				*s = '\0';
X				strncpy(name, envname, NAMELEN);
X				envp = s + 1;
X			}
X			else if (strncmp(envp, "port=", s - envp + 1) == 0) {
X				use_port = s + 1;
X				Test_port = atoi(use_port);
X				if ((s = index(envp, ',')) == NULL) {
X					*envp = '\0';
X					break;
X				}
X				*s = '\0';
X				envp = s + 1;
X			}
X			else if (strncmp(envp, "host=", s - envp + 1) == 0) {
X				Sock_host = s + 1;
X				if ((s = index(envp, ',')) == NULL) {
X					*envp = '\0';
X					break;
X				}
X				*s = '\0';
X				envp = s + 1;
X			}
X# ifdef INTERNET
X			else if (strncmp(envp, "message=", s - envp + 1) == 0) {
X				Send_message = s + 1;
X				if ((s = index(envp, ',')) == NULL) {
X					*envp = '\0';
X					break;
X				}
X				*s = '\0';
X				envp = s + 1;
X			}
X# endif
X			else if (strncmp(envp, "team=", s - envp + 1) == 0) {
X				team = *(s + 1);
X				if (!isdigit(team))
X					team = ' ';
X				if ((s = index(envp, ',')) == NULL) {
X					*envp = '\0';
X					break;
X				}
X				*s = '\0';
X				envp = s + 1;
X			}			/* must be last option */
X			else if (strncmp(envp, "mapkey=", s - envp + 1) == 0) {
X				for (s = s + 1; *s != '\0'; s += 2) {
X					map_key[(unsigned int) *s] = *(s + 1);
X					if (*(s + 1) == '\0') {
X						break;
X					}
X				}
X				*envp = '\0';
X				break;
X			} else {
X				*s = '\0';
X				printf("unknown option %s\n", envp);
X				if ((s = index(envp, ',')) == NULL) {
X					*envp = '\0';
X					break;
X				}
X				envp = s + 1;
X			}
X		}
X		if (*envp != '\0')
X			if (envname == NULL)
X				strncpy(name, envp, NAMELEN);
X			else
X				printf("unknown option %s\n", envp);
X	}
X	return enter_status;
X}
X
Xfill_in_blanks()
X{
X	register int	i;
X	register char	*cp;
X
Xagain:
X	if (name[0] != '\0') {
X		printf("Entering as '%s'", name);
X		if (team != ' ')
X			printf(" on team %c.\n", team);
X		else
X			putchar('\n');
X	} else {
X		printf("Enter your code name: ");
X		if (fgets(name, NAMELEN, stdin) == NULL)
X			exit(1);
X	}
X	rmnl(name);
X	if (name[0] == '\0') {
X		name[0] = '\0';
X		printf("You have to have a code name!\n");
X		goto again;
X	}
X	for (cp = name; *cp != '\0'; cp++)
X		if (!isprint(*cp)) {
X			name[0] = '\0';
X			printf("Illegal character in your code name.\n");
X			goto again;
X		}
X	if (team == ' ') {
X		printf("Enter your team (0-9 or nothing): ");
X		i = getchar();
X		if (isdigit(i))
X			team = i;
X		while (i != '\n' && i != EOF)
X			i = getchar();
X	}
X}
X
X#if	defined(BSD)
X#if	BSD < 43
Xchar *
Xstrpbrk(s, brk)
X	register char *s, *brk;
X{
X	register char *p;
X	register c;
X
X	while (c = *s) {
X		for (p = brk; *p; p++)
X			if (c == *p)
X				return (s);
X		s++;
X	}
X	return (0);
X}
X#endif
X#endif
END_OF_FILE
if test 19289 -ne `wc -c <'hunt.c'`; then
    echo shar: \"'hunt.c'\" unpacked with wrong size!
fi
# end of 'hunt.c'
fi
if test -f 'shots.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'shots.c'\"
else
echo shar: Extracting \"'shots.c'\" \(21015 characters\)
sed "s/^X//" >'shots.c' <<'END_OF_FILE'
X/*
X *  Hunt
X *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
X *  San Francisco, California
X *
X *  Copyright (c) 1985 Regents of the University of California.
X *  All rights reserved.  The Berkeley software License Agreement
X *  specifies the terms and conditions for redistribution.
X */
X
X# include	"hunt.h"
X# include	<signal.h>
X
X# define	PLUS_DELTA(x, max)	if (x < max) x++; else x--
X# define	MINUS_DELTA(x, min)	if (x > min) x--; else x++
X
X/*
X * moveshots:
X *	Move the shots already in the air, taking explosions into account
X */
Xmoveshots()
X{
X	register BULLET	*bp, *next;
X	register PLAYER	*pp;
X	register int	x, y;
X	register BULLET	*blist;
X
X	rollexpl();
X	if (Bullets == NULL)
X		goto ret;
X
X	/*
X	 * First we move through the bullet list BULSPD times, looking
X	 * for things we may have run into.  If we do run into
X	 * something, we set up the explosion and disappear, checking
X	 * for damage to any player who got in the way.
X	 */
X
X	blist = Bullets;
X	Bullets = NULL;
X	for (bp = blist; bp != NULL; bp = next) {
X		next = bp->b_next;
X		x = bp->b_x;
X		y = bp->b_y;
X		Maze[y][x] = bp->b_over;
X		for (pp = Player; pp < End_player; pp++)
X			check(pp, y, x);
X# ifdef MONITOR
X		for (pp = Monitor; pp < End_monitor; pp++)
X			check(pp, y, x);
X# endif MONITOR
X
X		switch (bp->b_type) {
X		  case SHOT:
X		  case GRENADE:
X		  case SATCHEL:
X		  case BOMB:
X			if (move_normal_shot(bp)) {
X				bp->b_next = Bullets;
X				Bullets = bp;
X			}
X			break;
X# ifdef OOZE
X		  case SLIME:
X			if (bp->b_expl || move_normal_shot(bp)) {
X				bp->b_next = Bullets;
X				Bullets = bp;
X			}
X			break;
X# endif OOZE
X# ifdef DRONE
X		  case DSHOT:
X			if (move_drone(bp)) {
X				bp->b_next = Bullets;
X				Bullets = bp;
X			}
X			break;
X# endif DRONE
X		  default:
X			bp->b_next = Bullets;
X			Bullets = bp;
X			break;
X		}
X	}
X
X	blist = Bullets;
X	Bullets = NULL;
X	for (bp = blist; bp != NULL; bp = next) {
X		next = bp->b_next;
X		if (!bp->b_expl) {
X			save_bullet(bp);
X# ifdef MONITOR
X			for (pp = Monitor; pp < End_monitor; pp++)
X				check(pp, bp->b_y, bp->b_x);
X# endif MONITOR
X# ifdef DRONE
X			if (bp->b_type == DSHOT)
X				for (pp = Player; pp < End_player; pp++)
X					if (pp->p_scan >= 0)
X						check(pp, bp->b_y, bp->b_x);
X# endif	DRONE
X			continue;
X		}
X
X		chkshot(bp, next);
X		free((char *) bp);
X	}
X
X	for (pp = Player; pp < End_player; pp++)
X		Maze[pp->p_y][pp->p_x] = pp->p_face;
X
Xret:
X# ifdef BOOTS
X	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
X		if (pp->p_flying >= 0)
X			move_flyer(pp);
X# endif	BOOTS
X	for (pp = Player; pp < End_player; pp++) {
X# ifdef FLY
X		if (pp->p_flying >= 0)
X			move_flyer(pp);
X# endif FLY
X		sendcom(pp, REFRESH);	/* Flush out the explosions */
X		look(pp);
X		sendcom(pp, REFRESH);
X	}
X# ifdef MONITOR
X	for (pp = Monitor; pp < End_monitor; pp++)
X		sendcom(pp, REFRESH);
X# endif MONITOR
X
X	return;
X}
X
X/*
X * move_normal_shot:
X *	Move a normal shot along its trajectory
X */
Xmove_normal_shot(bp)
Xregister BULLET	*bp;
X{
X	register int	i, x, y;
X	register PLAYER	*pp;
X
X	for (i = 0; i < BULSPD; i++) {
X		if (bp->b_expl)
X			break;
X
X		x = bp->b_x;
X		y = bp->b_y;
X
X		switch (bp->b_face) {
X		  case LEFTS:
X			x--;
X			break;
X		  case RIGHT:
X			x++;
X			break;
X		  case ABOVE:
X			y--;
X			break;
X		  case BELOW:
X			y++;
X			break;
X		}
X
X		switch (Maze[y][x]) {
X		  case SHOT:
X			if (rand_num(100) < 5) {
X				zapshot(Bullets, bp);
X				zapshot(bp->b_next, bp);
X			}
X			break;
X		  case GRENADE:
X			if (rand_num(100) < 10) {
X				zapshot(Bullets, bp);
X				zapshot(bp->b_next, bp);
X			}
X			break;
X# ifdef	REFLECT
X		  case WALL4:	/* reflecting walls */
X			switch (bp->b_face) {
X			  case LEFTS:
X				bp->b_face = BELOW;
X				break;
X			  case RIGHT:
X				bp->b_face = ABOVE;
X				break;
X			  case ABOVE:
X				bp->b_face = RIGHT;
X				break;
X			  case BELOW:
X				bp->b_face = LEFTS;
X				break;
X			}
X			Maze[y][x] = WALL5;
X# ifdef MONITOR
X			for (pp = Monitor; pp < End_monitor; pp++)
X				check(pp, y, x);
X# endif MONITOR
X			break;
X		  case WALL5:
X			switch (bp->b_face) {
X			  case LEFTS:
X				bp->b_face = ABOVE;
X				break;
X			  case RIGHT:
X				bp->b_face = BELOW;
X				break;
X			  case ABOVE:
X				bp->b_face = LEFTS;
X				break;
X			  case BELOW:
X				bp->b_face = RIGHT;
X				break;
X			}
X			Maze[y][x] = WALL4;
X# ifdef MONITOR
X			for (pp = Monitor; pp < End_monitor; pp++)
X				check(pp, y, x);
X# endif MONITOR
X			break;
X# endif REFLECT
X# ifdef RANDOM
X		  case DOOR:
X			switch (rand_num(4)) {
X			  case 0:
X				bp->b_face = ABOVE;
X				break;
X			  case 1:
X				bp->b_face = BELOW;
X				break;
X			  case 2:
X				bp->b_face = LEFTS;
X				break;
X			  case 3:
X				bp->b_face = RIGHT;
X				break;
X			}
X			break;
X# endif RANDOM
X# ifdef FLY
X		  case FLYER:
X			pp = play_at(y, x);
X			message(pp, "Zing!");
X			break;
X# endif FLY
X		  case LEFTS:
X		  case RIGHT:
X		  case BELOW:
X		  case ABOVE:
X			/*
X			 * give the person a chance to catch a
X			 * grenade if s/he is facing it
X			 */
X			pp = play_at(y, x);
X			pp->p_ident->i_shot += bp->b_charge;
X			if (opposite(bp->b_face, Maze[y][x])) {
X			    if (rand_num(100) < 10) {
X				if (bp->b_owner != NULL)
X					message(bp->b_owner,
X					    "Your charge was absorbed!");
X				if (bp->b_score != NULL)
X					bp->b_score->i_robbed += bp->b_charge;
X				pp->p_ammo += bp->b_charge;
X				if (pp->p_damage + bp->b_size * MINDAM
X				    > pp->p_damcap)
X					pp->p_ident->i_saved++;
X				message(pp, "Absorbed charge (good shield!)");
X				pp->p_ident->i_absorbed += bp->b_charge;
X				free((char *) bp);
X				(void) sprintf(Buf, "%3d", pp->p_ammo);
X				cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
X				outstr(pp, Buf, 3);
X				return FALSE;
X			    }
X			    pp->p_ident->i_faced += bp->b_charge;
X			}
X			/*
X			 * Small chance that the bullet just misses the
X			 * person.  If so, the bullet just goes on its
X			 * merry way without exploding.
X			 */
X			if (rand_num(100) < 5) {
X				pp->p_ident->i_ducked += bp->b_charge;
X				if (pp->p_damage + bp->b_size * MINDAM
X				    > pp->p_damcap)
X					pp->p_ident->i_saved++;
X				if (bp->b_score != NULL)
X					bp->b_score->i_missed += bp->b_charge;
X				message(pp, "Zing!");
X				if (bp->b_owner == NULL)
X					break;
X				message(bp->b_owner,
X					((bp->b_score->i_missed & 0x7) == 0x7) ?
X					"My!  What a bad shot you are!" :
X					"Missed him");
X				break;
X			}
X			/*
X			 * The shot hit that sucker!  Blow it up.
X			 */
X			/* FALLTHROUGH */
X# ifndef RANDOM
X		  case DOOR:
X# endif RANDOM
X		  case WALL1:
X		  case WALL2:
X		  case WALL3:
X			bp->b_expl = TRUE;
X			break;
X		}
X
X		bp->b_x = x;
X		bp->b_y = y;
X	}
X	return TRUE;
X}
X
X# ifdef	DRONE
X/*
X * move_drone:
X *	Move the drone to the next square
X */
Xmove_drone(bp)
Xregister BULLET	*bp;
X{
X	register int	mask, count;
X	register int	n, dir;
X	register PLAYER	*pp;
X
X	/*
X	 * See if we can give someone a blast
X	 */
X	if (isplayer(Maze[bp->b_y][bp->b_x - 1])) {
X		dir = WEST;
X		goto drone_move;
X	}
X	if (isplayer(Maze[bp->b_y - 1][bp->b_x])) {
X		dir = NORTH;
X		goto drone_move;
X	}
X	if (isplayer(Maze[bp->b_y + 1][bp->b_x])) {
X		dir = SOUTH;
X		goto drone_move;
X	}
X	if (isplayer(Maze[bp->b_y][bp->b_x + 1])) {
X		dir = EAST;
X		goto drone_move;
X	}
X
X	/*
X	 * Find out what directions are clear
X	 */
X	mask = count = 0;
X	if (!iswall(bp->b_y, bp->b_x - 1))
X		mask |= WEST, count++;
X	if (!iswall(bp->b_y - 1, bp->b_x))
X		mask |= NORTH, count++;
X	if (!iswall(bp->b_y + 1, bp->b_x))
X		mask |= SOUTH, count++;
X	if (!iswall(bp->b_y, bp->b_x + 1))
X		mask |= EAST, count++;
X
X	/*
X	 * All blocked up, just you wait
X	 */
X	if (count == 0)
X		return TRUE;
X
X	/*
X	 * Only one way to go.
X	 */
X	if (count == 1) {
X		dir = mask;
X		goto drone_move;
X	}
X
X	/*
X	 * Get rid of the direction that we came from
X	 */
X	switch (bp->b_face) {
X	  case LEFTS:
X		if (mask & EAST)
X			mask &= ~EAST, count--;
X		break;
X	  case RIGHT:
X		if (mask & WEST)
X			mask &= ~WEST, count--;
X		break;
X	  case ABOVE:
X		if (mask & SOUTH)
X			mask &= ~SOUTH, count--;
X		break;
X	  case BELOW:
X		if (mask & NORTH)
X			mask &= ~NORTH, count--;
X		break;
X	}
X
X	/*
X	 * Pick one of the remaining directions
X	 */
X	n = rand_num(count);
X	if (n >= 0 && mask & NORTH)
X		dir = NORTH, n--;
X	if (n >= 0 && mask & SOUTH)
X		dir = SOUTH, n--;
X	if (n >= 0 && mask & EAST)
X		dir = EAST, n--;
X	if (n >= 0 && mask & WEST)
X		dir = WEST, n--;
X
X	/*
X	 * Now that we know the direction of movement,
X	 * just update the position of the drone
X	 */
Xdrone_move:
X	switch (dir) {
X	  case WEST:
X		bp->b_x--;
X		bp->b_face = LEFTS;
X		break;
X	  case EAST:
X		bp->b_x++;
X		bp->b_face = RIGHT;
X		break;
X	  case NORTH:
X		bp->b_y--;
X		bp->b_face = ABOVE;
X		break;
X	  case SOUTH:
X		bp->b_y++;
X		bp->b_face = BELOW;
X		break;
X	}
X	switch (Maze[bp->b_y][bp->b_x]) {
X	  case LEFTS:
X	  case RIGHT:
X	  case BELOW:
X	  case ABOVE:
X		/*
X		 * give the person a chance to catch a
X		 * drone if s/he is facing it
X		 */
X		if (rand_num(100) < 1 &&
X		opposite(bp->b_face, Maze[bp->b_y][bp->b_x])) {
X			pp = play_at(bp->b_y, bp->b_x);
X			pp->p_ammo += bp->b_charge;
X			message(pp, "**** Absorbed drone ****");
X			free((char *) bp);
X			(void) sprintf(Buf, "%3d", pp->p_ammo);
X			cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
X			outstr(pp, Buf, 3);
X			return FALSE;
X		}
X		bp->b_expl = TRUE;
X		break;
X	}
X	return TRUE;
X}
X# endif DRONE
X
X/*
X * save_bullet:
X *	Put this bullet back onto the bullet list
X */
Xsave_bullet(bp)
Xregister BULLET	*bp;
X{
X	bp->b_over = Maze[bp->b_y][bp->b_x];
X	switch (bp->b_over) {
X	  case SHOT:
X	  case GRENADE:
X	  case SATCHEL:
X	  case BOMB:
X# ifdef OOZE
X	  case SLIME:
X# ifdef VOLCANO
X	  case LAVA:
X# endif VOLCANO
X# endif OOZE
X# ifdef DRONE
X	  case DSHOT:
X# endif DRONE
X		find_under(Bullets, bp);
X		break;
X	}
X
X	switch (bp->b_over) {
X	  case LEFTS:
X	  case RIGHT:
X	  case ABOVE:
X	  case BELOW:
X# ifdef FLY
X	  case FLYER:
X# endif FLY
X		mark_player(bp);
X		break;
X# ifdef BOOTS
X	  case BOOT:
X	  case BOOT_PAIR:
X		mark_boot(bp);
X# endif BOOTS
X		
X	  default:
X		Maze[bp->b_y][bp->b_x] = bp->b_type;
X		break;
X	}
X
X	bp->b_next = Bullets;
X	Bullets = bp;
X}
X
X/*
X * move_flyer:
X *	Update the position of a player in flight
X */
Xmove_flyer(pp)
Xregister PLAYER	*pp;
X{
X	register int	x, y;
X
X	if (pp->p_undershot) {
X		fixshots(pp->p_y, pp->p_x, pp->p_over);
X		pp->p_undershot = FALSE;
X	}
X	Maze[pp->p_y][pp->p_x] = pp->p_over;
X	x = pp->p_x + pp->p_flyx;
X	y = pp->p_y + pp->p_flyy;
X	if (x < 1) {
X		x = 1 - x;
X		pp->p_flyx = -pp->p_flyx;
X	}
X	else if (x > WIDTH - 2) {
X		x = (WIDTH - 2) - (x - (WIDTH - 2));
X		pp->p_flyx = -pp->p_flyx;
X	}
X	if (y < 1) {
X		y = 1 - y;
X		pp->p_flyy = -pp->p_flyy;
X	}
X	else if (y > HEIGHT - 2) {
X		y = (HEIGHT - 2) - (y - (HEIGHT - 2));
X		pp->p_flyy = -pp->p_flyy;
X	}
Xagain:
X	switch (Maze[y][x]) {
X	  default:
X		switch (rand_num(4)) {
X		  case 0:
X			PLUS_DELTA(x, WIDTH - 2);
X			break;
X		  case 1:
X			MINUS_DELTA(x, 1);
X			break;
X		  case 2:
X			PLUS_DELTA(y, HEIGHT - 2);
X			break;
X		  case 3:
X			MINUS_DELTA(y, 1);
X			break;
X		}
X		goto again;
X	  case WALL1:
X	  case WALL2:
X	  case WALL3:
X# ifdef	REFLECT
X	  case WALL4:
X	  case WALL5:
X# endif REFLECT
X# ifdef	RANDOM
X	  case DOOR:
X# endif	RANDOM
X		if (pp->p_flying == 0)
X			pp->p_flying++;
X		break;
X	  case SPACE:
X		break;
X	}
X	pp->p_y = y;
X	pp->p_x = x;
X	if (pp->p_flying-- == 0) {
X# ifdef BOOTS
X		if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
X# endif
X			checkdam(pp, (PLAYER *) NULL, (IDENT *) NULL,
X				rand_num(pp->p_damage / 5), FALL);
X			pp->p_face = rand_dir();
X			showstat(pp);
X# ifdef BOOTS
X		}
X		else {
X			if (Maze[y][x] == BOOT)
X				pp->p_face = BOOT_PAIR;
X			Maze[y][x] = SPACE;
X		}
X# endif BOOTS
X	}
X	pp->p_over = Maze[y][x];
X	Maze[y][x] = pp->p_face;
X	showexpl(y, x, pp->p_face);
X}
X
X/*
X * chkshot
X *	Handle explosions
X */
Xchkshot(bp, next)
Xregister BULLET	*bp;
XBULLET		*next;
X{
X	register int	y, x;
X	register int	dy, dx, absdy;
X	register int	delta, damage;
X	register char	expl;
X	register PLAYER	*pp;
X
X	switch (bp->b_type) {
X	  case SHOT:
X	  case MINE:
X	  case GRENADE:
X	  case GMINE:
X	  case SATCHEL:
X	  case BOMB:
X		delta = bp->b_size - 1;
X		break;
X# ifdef	OOZE
X	  case SLIME:
X# ifdef VOLCANO
X	  case LAVA:
X# endif VOLCANO
X		chkslime(bp, next);
X		return;
X# endif	OOZE
X# ifdef DRONE
X	  case DSHOT:
X		bp->b_type = SLIME;
X		chkslime(bp, next);
X		return;
X# endif DRONE
X	}
X	for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
X		if (y < 0 || y >= HEIGHT)
X			continue;
X		dy = y - bp->b_y;
X		absdy = (dy < 0) ? -dy : dy;
X		for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
X			if (x < 0 || x >= WIDTH)
X				continue;
X			dx = x - bp->b_x;
X			if (dx == 0)
X				expl = (dy == 0) ? '*' : '|';
X			else if (dy == 0)
X				expl = '-';
X			else if (dx == dy)
X				expl = '\\';
X			else if (dx == -dy)
X				expl = '/';
X			else
X				expl = '*';
X			showexpl(y, x, expl);
X			switch (Maze[y][x]) {
X			  case LEFTS:
X			  case RIGHT:
X			  case ABOVE:
X			  case BELOW:
X# ifdef FLY
X			  case FLYER:
X# endif FLY
X				if (dx < 0)
X					dx = -dx;
X				if (absdy > dx)
X					damage = bp->b_size - absdy;
X				else
X					damage = bp->b_size - dx;
X				pp = play_at(y, x);
X				checkdam(pp, bp->b_owner, bp->b_score,
X					damage * MINDAM, bp->b_type);
X				break;
X			  case GMINE:
X			  case MINE:
X				add_shot((Maze[y][x] == GMINE) ?
X					GRENADE : SHOT,
X					y, x, LEFTS,
X					(Maze[y][x] == GMINE) ?
X					GRENREQ : BULREQ,
X					(PLAYER *) NULL, TRUE, SPACE);
X				Maze[y][x] = SPACE;
X				break;
X			}
X		}
X	}
X}
X
X# ifdef	OOZE
X/*
X * chkslime:
X *	handle slime shot exploding
X */
Xchkslime(bp, next)
Xregister BULLET	*bp;
XBULLET		*next;
X{
X	register BULLET	*nbp;
X
X	switch (Maze[bp->b_y][bp->b_x]) {
X	  case WALL1:
X	  case WALL2:
X	  case WALL3:
X# ifdef	REFLECT
X	  case WALL4:
X	  case WALL5:
X# endif REFLECT
X# ifdef	RANDOM
X	  case DOOR:
X# endif	RANDOM
X		switch (bp->b_face) {
X		  case LEFTS:
X			bp->b_x++;
X			break;
X		  case RIGHT:
X			bp->b_x--;
X			break;
X		  case ABOVE:
X			bp->b_y++;
X			break;
X		  case BELOW:
X			bp->b_y--;
X			break;
X		}
X		break;
X	}
X	nbp = (BULLET *) malloc(sizeof (BULLET));
X	*nbp = *bp;
X# ifdef VOLCANO
X	move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next);
X# else VOLCANO
X	move_slime(nbp, SLIMESPEED, next);
X# endif VOLCANO
X}
X
X/*
X * move_slime:
X *	move the given slime shot speed times and add it back if
X *	it hasn't fizzled yet
X */
Xmove_slime(bp, speed, next)
Xregister BULLET	*bp;
Xregister int	speed;
XBULLET		*next;
X{
X	register int	i, j, dirmask, count;
X	register PLAYER	*pp;
X	register BULLET	*nbp;
X
X	if (speed == 0) {
X		if (bp->b_charge <= 0)
X			free((char *) bp);
X		else
X			save_bullet(bp);
X		return;
X	}
X
X# ifdef VOLCANO
X	showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
X# else VOLCANO
X	showexpl(bp->b_y, bp->b_x, '*');
X# endif VOLCANO
X	switch (Maze[bp->b_y][bp->b_x]) {
X	  case LEFTS:
X	  case RIGHT:
X	  case ABOVE:
X	  case BELOW:
X# ifdef FLY
X	  case FLYER:
X# endif FLY
X		pp = play_at(bp->b_y, bp->b_x);
X		message(pp, "You've been slimed.");
X		checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type);
X		break;
X	  case SHOT:
X	  case GRENADE:
X	  case SATCHEL:
X	  case BOMB:
X# ifdef DRONE
X	  case DSHOT:
X# endif DRONE
X		explshot(next, bp->b_y, bp->b_x);
X		explshot(Bullets, bp->b_y, bp->b_x);
X		break;
X	}
X
X	if (--bp->b_charge <= 0) {
X		free((char *) bp);
X		return;
X	}
X
X	dirmask = 0;
X	count = 0;
X	switch (bp->b_face) {
X	  case LEFTS:
X		if (!iswall(bp->b_y, bp->b_x - 1))
X			dirmask |= WEST, count++;
X		if (!iswall(bp->b_y - 1, bp->b_x))
X			dirmask |= NORTH, count++;
X		if (!iswall(bp->b_y + 1, bp->b_x))
X			dirmask |= SOUTH, count++;
X		if (dirmask == 0)
X			if (!iswall(bp->b_y, bp->b_x + 1))
X				dirmask |= EAST, count++;
X		break;
X	  case RIGHT:
X		if (!iswall(bp->b_y, bp->b_x + 1))
X			dirmask |= EAST, count++;
X		if (!iswall(bp->b_y - 1, bp->b_x))
X			dirmask |= NORTH, count++;
X		if (!iswall(bp->b_y + 1, bp->b_x))
X			dirmask |= SOUTH, count++;
X		if (dirmask == 0)
X			if (!iswall(bp->b_y, bp->b_x - 1))
X				dirmask |= WEST, count++;
X		break;
X	  case ABOVE:
X		if (!iswall(bp->b_y - 1, bp->b_x))
X			dirmask |= NORTH, count++;
X		if (!iswall(bp->b_y, bp->b_x - 1))
X			dirmask |= WEST, count++;
X		if (!iswall(bp->b_y, bp->b_x + 1))
X			dirmask |= EAST, count++;
X		if (dirmask == 0)
X			if (!iswall(bp->b_y + 1, bp->b_x))
X				dirmask |= SOUTH, count++;
X		break;
X	  case BELOW:
X		if (!iswall(bp->b_y + 1, bp->b_x))
X			dirmask |= SOUTH, count++;
X		if (!iswall(bp->b_y, bp->b_x - 1))
X			dirmask |= WEST, count++;
X		if (!iswall(bp->b_y, bp->b_x + 1))
X			dirmask |= EAST, count++;
X		if (dirmask == 0)
X			if (!iswall(bp->b_y - 1, bp->b_x))
X				dirmask |= NORTH, count++;
X		break;
X	}
X	if (count == 0) {
X		/*
X		 * No place to go.  Just sit here for a while and wait
X		 * for adjacent squares to clear out.
X		 */
X		save_bullet(bp);
X		return;
X	}
X	if (bp->b_charge < count) {
X		/* Only bp->b_charge paths may be taken */
X		while (count > bp->b_charge) {
X			if (dirmask & WEST)
X				dirmask &= ~WEST;
X			else if (dirmask & EAST)
X				dirmask &= ~EAST;
X			else if (dirmask & NORTH)
X				dirmask &= ~NORTH;
X			else if (dirmask & SOUTH)
X				dirmask &= ~SOUTH;
X			count--;
X		}
X	}
X
X	i = bp->b_charge / count;
X	j = bp->b_charge % count;
X	if (dirmask & WEST) {
X		count--;
X		nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
X			i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE);
X		move_slime(nbp, speed - 1, next);
X	}
X	if (dirmask & EAST) {
X		count--;
X		nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
X			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
X			bp->b_score, TRUE, SPACE);
X		move_slime(nbp, speed - 1, next);
X	}
X	if (dirmask & NORTH) {
X		count--;
X		nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
X			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
X			bp->b_score, TRUE, SPACE);
X		move_slime(nbp, speed - 1, next);
X	}
X	if (dirmask & SOUTH) {
X		count--;
X		nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
X			(count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
X			bp->b_score, TRUE, SPACE);
X		move_slime(nbp, speed - 1, next);
X	}
X
X	free((char *) bp);
X}
X
X/*
X * iswall:
X *	returns whether the given location is a wall
X */
Xiswall(y, x)
Xregister int	y, x;
X{
X	if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
X		return TRUE;
X	switch (Maze[y][x]) {
X	  case WALL1:
X	  case WALL2:
X	  case WALL3:
X# ifdef	REFLECT
X	  case WALL4:
X	  case WALL5:
X# endif	REFLECT
X# ifdef	RANDOM
X	  case DOOR:
X# endif	RANDOM
X# ifdef OOZE
X	  case SLIME:
X# ifdef VOLCANO
X	  case LAVA:
X# endif VOLCANO
X# endif OOZE
X		return TRUE;
X	}
X	return FALSE;
X}
X# endif	OOZE
X
X/*
X * zapshot:
X *	Take a shot out of the air.
X */
Xzapshot(blist, obp)
Xregister BULLET	*blist, *obp;
X{
X	register BULLET	*bp;
X	register FLAG	explode;
X
X	explode = FALSE;
X	for (bp = blist; bp != NULL; bp = bp->b_next) {
X		if (bp->b_x != obp->b_x || bp->b_y != obp->b_y)
X			continue;
X		if (bp->b_face == obp->b_face)
X			continue;
X		explode = TRUE;
X		break;
X	}
X	if (!explode)
X		return;
X	explshot(blist, obp->b_y, obp->b_x);
X}
X
X/*
X * explshot -
X *	Make all shots at this location blow up
X */
Xexplshot(blist, y, x)
Xregister BULLET	*blist;
Xregister int	y, x;
X{
X	register BULLET	*bp;
X
X	for (bp = blist; bp != NULL; bp = bp->b_next)
X		if (bp->b_x == x && bp->b_y == y) {
X			bp->b_expl = TRUE;
X			if (bp->b_owner != NULL)
X				message(bp->b_owner, "Shot intercepted");
X		}
X}
X
X/*
X * play_at:
X *	Return a pointer to the player at the given location
X */
XPLAYER *
Xplay_at(y, x)
Xregister int	y, x;
X{
X	register PLAYER	*pp;
X
X	for (pp = Player; pp < End_player; pp++)
X		if (pp->p_x == x && pp->p_y == y)
X			return pp;
X	fprintf(stderr, "driver: couldn't find player at (%d,%d)\n", x, y);
X	abort();
X	/* NOTREACHED */
X}
X
X/*
X * opposite:
X *	Return TRUE if the bullet direction faces the opposite direction
X *	of the player in the maze
X */
Xopposite(face, dir)
Xint	face;
Xchar	dir;
X{
X	switch (face) {
X	  case LEFTS:
X		return (dir == RIGHT);
X	  case RIGHT:
X		return (dir == LEFTS);
X	  case ABOVE:
X		return (dir == BELOW);
X	  case BELOW:
X		return (dir == ABOVE);
X	  default:
X		return FALSE;
X	}
X}
X
X/*
X * is_bullet:
X *	Is there a bullet at the given coordinates?  If so, return
X *	a pointer to the bullet, otherwise return NULL
X */
XBULLET *
Xis_bullet(y, x)
Xregister int	y, x;
X{
X	register BULLET	*bp;
X
X	for (bp = Bullets; bp != NULL; bp = bp->b_next)
X		if (bp->b_y == y && bp->b_x == x)
X			return bp;
X	return NULL;
X}
X
X/*
X * fixshots:
X *	change the underlying character of the shots at a location
X *	to the given character.
X */
Xfixshots(y, x, over)
Xregister int	y, x;
Xchar		over;
X{
X	register BULLET	*bp;
X
X	for (bp = Bullets; bp != NULL; bp = bp->b_next)
X		if (bp->b_y == y && bp->b_x == x)
X			bp->b_over = over;
X}
X
X/*
X * find_under:
X *	find the underlying character for a bullet when it lands
X *	on another bullet.
X */
Xfind_under(blist, bp)
Xregister BULLET	*blist, *bp;
X{
X	register BULLET	*nbp;
X
X	for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
X		if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
X			bp->b_over = nbp->b_over;
X			break;
X		}
X}
X
X/*
X * mark_player:
X *	mark a player as under a shot
X */
Xmark_player(bp)
Xregister BULLET	*bp;
X{
X	register PLAYER	*pp;
X
X	for (pp = Player; pp < End_player; pp++)
X		if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
X			pp->p_undershot = TRUE;
X			break;
X		}
X}
X
X# ifdef BOOTS
X/*
X * mark_boot:
X *	mark a boot as under a shot
X */
Xmark_boot(bp)
Xregister BULLET	*bp;
X{
X	register PLAYER	*pp;
X
X	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
X		if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
X			pp->p_undershot = TRUE;
X			break;
X		}
X}
X# endif BOOTS
END_OF_FILE
if test 21015 -ne `wc -c <'shots.c'`; then
    echo shar: \"'shots.c'\" unpacked with wrong size!
fi
# end of 'shots.c'
fi
echo shar: End of archive 1 \(of 4\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0