summaryrefslogtreecommitdiffstats
path: root/hunt
diff options
context:
space:
mode:
authormrg <mrg@NetBSD.org>1997-10-04 09:00:13 +0000
committermrg <mrg@NetBSD.org>1997-10-04 09:00:13 +0000
commite9152f6d21d5a8b1e02922e0fc95b71fc21cbd92 (patch)
tree3e1b32f0c0c61414ebd853c92584cd9d95a99acc /hunt
parent6f367f8f8be268d527e585867c1c42ffbb07668c (diff)
downloadbsdgames-darwin-e9152f6d21d5a8b1e02922e0fc95b71fc21cbd92.tar.gz
bsdgames-darwin-e9152f6d21d5a8b1e02922e0fc95b71fc21cbd92.tar.zst
bsdgames-darwin-e9152f6d21d5a8b1e02922e0fc95b71fc21cbd92.zip
hunt version 1993-07-17
Diffstat (limited to 'hunt')
-rw-r--r--hunt/README175
-rw-r--r--hunt/hunt/connect.c41
-rw-r--r--hunt/hunt/hunt.6378
-rw-r--r--hunt/hunt/hunt.c1072
-rw-r--r--hunt/hunt/otto.c585
-rw-r--r--hunt/hunt/playit.c628
-rw-r--r--hunt/huntd/answer.c389
-rw-r--r--hunt/huntd/bsd.h15
-rw-r--r--hunt/huntd/ctl.c52
-rw-r--r--hunt/huntd/ctl_transact.c101
-rw-r--r--hunt/huntd/draw.c365
-rw-r--r--hunt/huntd/driver.c956
-rw-r--r--hunt/huntd/execute.c556
-rw-r--r--hunt/huntd/expl.c211
-rw-r--r--hunt/huntd/extern.c64
-rw-r--r--hunt/huntd/faketalk.c217
-rw-r--r--hunt/huntd/get_names.c125
-rw-r--r--hunt/huntd/hunt.h400
-rw-r--r--hunt/huntd/huntd.698
-rw-r--r--hunt/huntd/makemaze.c191
-rw-r--r--hunt/huntd/pathname.c35
-rw-r--r--hunt/huntd/shots.c1096
-rw-r--r--hunt/huntd/talk_ctl.h80
-rw-r--r--hunt/huntd/terminal.c110
24 files changed, 7940 insertions, 0 deletions
diff --git a/hunt/README b/hunt/README
new file mode 100644
index 00000000..3bc2728c
--- /dev/null
+++ b/hunt/README
@@ -0,0 +1,175 @@
+What *is* hunt?
+
+ Hunt is a multi-player search-and-destroy game that takes place
+ in a maze. The game may either be slow and strategic or fast
+ and tactical, depending on how familiar the players are with the
+ keyboard commands.
+
+Distribution Policy:
+
+ Hunt is part of the user-contributed software distributed by
+ Berkeley in 4BSD. The sources are copyrighted by the authors
+ and the University of California. You may redistribute freely
+ as long as the copyright notices are retained.
+
+Words of Warning:
+
+ hunt uses the socket mechanism of 4BSD Unix, so if you are on
+ System V (my sympathies), you're on your own.
+ If your machine does not permit non-setuid-root processes to
+ broadcast UDP packets, then hunt uses a *very* inefficient
+ method for locating the hunt server: it sends a packet
+ to every host on your network. If your machine falls
+ into this category, we strongly recommend that you use
+ either standalone or inetd mode *and* start hunt by
+ specifying the hunt server host.
+ hunt can be configured to use Unix-domain sockets, but that
+ code has not been tested in recent memory. Also, since
+ 4.2BSD Unix-domain sockets are buggy, running hunt on
+ 4.2BSD with Unix-domain sockets will probably crash
+ your system. If you want to experiment, feel free to
+ do so. However, don't say I didn't warn you :-).
+ hunt uses a fair amount of CPU time, both in user time (for
+ computing interactions) and system time (for processing
+ terminal interrupts). We found that a VAX 750 can
+ support about three users before the system is
+ noticeably impacted. The number goes up to about 8 or
+ 10 for a VAX 8650. On a network of Sun 3/50's with the
+ server running on a 3/280, things work much more
+ smoothly as the computing load is distributed across
+ many machines.
+ hunt may be dangerous to your health. "Arthritic pain" and
+ "lack of circulation" in fingers have been reported by
+ hunt abusers. Hunt may also be addictive, and the
+ withdrawal symptoms are not pretty :-)
+
+Installation:
+
+ 1. Edit file "Makefile" and make sure the options selected are
+ reasonable. There are four "make" variables that you
+ should check: GAME_PARAM, SYSCFLAGS, SYSLDFLAGS, and DEFS.
+ GAME_PARAM controls what features of the game will be
+ compiled in (e.g. reflecting walls). The optional features
+ are listed in comments above where GAME_PARAM is defined.
+ If you want to try them, just add the ones you want to the
+ GAME_PARAM definition.
+
+ DEFS is where most system configuration is described.
+ If your system is 4.3BSD, Sun, Ultrix, Convex, HPUX
+ v6.0.1, or SGI, you're in luck. We provide the
+ appropriate definitions for these systems and you just
+ need to select one of them (e.g. if you have an Ultrix
+ system, just change the line
+ DEFS= $(GAME_PARAM) $(DEFS_43)
+ to
+ DEFS= $(GAME_PARAM) $(DEFS_ULTRIX)
+ ). If your system is *not* listed above, then you may
+ need to do some experiments. All of the options are
+ documented in the Makefile, be brave.
+
+ SYSCFLAGS and SYSLDFLAGS are used for "unusual" systems
+ and you probably won't need to deal with it. An
+ example of an unusual system is the Silicon Graphics
+ IRIS, which keeps the network socket code in a BSD
+ emulation library that is in -lbsd. Edit these only if
+ you *know* your system is "different."
+
+ 2. Edit file "Makefile" and look at the "install:" target. By
+ default, files are installed in /usr/games,
+ /usr/games/lib, and /usr/man/man6, which are "standard"
+ locations for games. If your system has a local games
+ directory, you'll need to change these.
+ 3. Edit file "pathname.c" and make sure the file names and port
+ numbers are reasonable. You can ignore the first set
+ of variables as they are used only for debugging
+ purposes. The second set is used in the installed
+ version of hunt. The important variables are "Driver"
+ (where the server is kept), "Test_port" (the Internet
+ UDP port number that new players should use to contact
+ the server), and "Stat_file" (where scoring statistics
+ and body counts are written). The only tricky variable
+ here is "Test_port". The default value is chosen so
+ that it is unlikely to conflict with other service port
+ numbers, but you can change it if you want to.
+ 4. Type "make install", which will compile and install the
+ programs and manual pages. Now you're almost ready to
+ go (see next section). There may be some warnings during
+ compilation. Ignore them.
+
+Setting up the network:
+
+ Hunt may be set up in one of three modes: standalone, inetd, or
+ nothing. In "standalone" mode, there is always a hunt server
+ running on a server machine. All players who enter the game
+ will be talking to this server. This is the mode we use at
+ UCSF. The cost is one entry in the process table on the server
+ machine. In "inetd" mode, the server is started via inetd.
+ Again, only one machine should be set up to answer game
+ requests. The cost is having to edit a few system files. In
+ "nothing" mode, no server is running when there is no one
+ playing. The first person to enter hunt will automatically
+ start up a server on his machine. This, of course, gives him
+ an unfair advantage. Also, there may be race conditions such
+ that players end up in different games. The choice of which
+ mode to use depends on site configuration and politics. We
+ recommend using "standalone" mode because it is simple to set
+ up and starts up rapidly.
+
+ -----
+
+ FOR STANDALONE MODE, put these lines in /etc/rc.local on the
+ server machine. THERE SHOULD ONLY BE ONE SERVER MACHINE!
+
+ # start up the hunt daemon if present
+ if [ -f /usr/games/lib/huntd ]; then
+ /usr/games/lib/huntd -s & (echo -n ' huntd') >/dev/console
+ fi
+
+ Also, you should start one up (on the off chance that you will
+ want to test this mess :-) by typing "/usr/games/lib/hunt -s".
+
+ -----
+
+ FOR INETD MODE, then things get more complicated. You need to
+ edit both /etc/services and /etc/inetd.conf. In /etc/services,
+ add the line
+
+ hunt 26740/udp
+
+ 26740 corresponds to the default "Test_port". If you changed
+ that variable, then you should put whatever value you used here
+ as well. In /etc/inetd.conf, add the line
+
+ hunt dgram udp wait nobody /usr/games/lib/huntd huntd
+
+ This works for 4.3BSD. I don't remember the configuration file
+ format for 4.2BSD inetd.
+
+ See the huntd.6 manual page for more details.
+
+ -----
+
+ FOR NOTHING MODE, do nothing.
+
+Testing:
+ Now you are ready to test the code. Type "/usr/games/hunt" or
+ whatever you call the hunt executable. You should be prompted
+ for your name and team. Then you should get the display of a
+ maze. At this point, you should read the manual page :-).
+
+======
+
+Hunt is not officially supported by anyone anywhere (that I know of);
+however, bug reports will be read and bug fixes/enhancements may be
+sent out at irregular intervals. Send no flames, just money. Happy
+hunting.
+
+ Conrad Huang
+ conrad@cgl.ucsf.edu
+ Greg Couch
+ gregc@cgl.ucsf.edu
+ October 17, 1988
+
+P.S. The authors of the game want to emphasize that this version of hunt
+was started over eight years ago, and the programming style exhibited here
+in no way reflects the current programming practices of the authors.
diff --git a/hunt/hunt/connect.c b/hunt/hunt/connect.c
new file mode 100644
index 00000000..587f8b40
--- /dev/null
+++ b/hunt/hunt/connect.c
@@ -0,0 +1,41 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+# include <signal.h>
+
+do_connect(name, team, enter_status)
+char *name;
+char team;
+long enter_status;
+{
+ static long uid;
+ static long mode;
+ extern char *ttyname();
+
+ if (uid == 0)
+ uid = htonl(getuid());
+ (void) write(Socket, (char *) &uid, LONGLEN);
+ (void) write(Socket, name, NAMELEN);
+ (void) write(Socket, &team, 1);
+ enter_status = htonl(enter_status);
+ (void) write(Socket, (char *) &enter_status, LONGLEN);
+ (void) strcpy(Buf, ttyname(fileno(stderr)));
+ (void) write(Socket, Buf, NAMELEN);
+# ifdef INTERNET
+ if (Send_message != NULL)
+ mode = C_MESSAGE;
+ else
+# endif
+# ifdef MONITOR
+ if (Am_monitor)
+ mode = C_MONITOR;
+ else
+# endif
+ mode = C_PLAYER;
+ mode = htonl(mode);
+ (void) write(Socket, (char *) &mode, sizeof mode);
+}
diff --git a/hunt/hunt/hunt.6 b/hunt/hunt/hunt.6
new file mode 100644
index 00000000..5394e1ec
--- /dev/null
+++ b/hunt/hunt/hunt.6
@@ -0,0 +1,378 @@
+.\" hunt
+.\" Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+.\" San Francisco, California
+.\"
+.\" Copyright (c) 1985 Regents of the University of California.
+.\" All rights reserved. The Berkeley software License Agreement
+.\" specifies the terms and conditions for redistribution.
+.\"
+.TH HUNT 6 "21 August 1986"
+.UC 4
+.SH NAME
+hunt \- a multi-player multi-terminal game
+.SH SYNOPSIS
+\fB/usr/games/hunt\fP [ \fB\-qmcsfbS\fP ] [ \fB\-n\fP name ] [ \fB\-t\fP team ] [ \fB\-p\fP port ] [ \fB\-w\fP message ] [ host ]
+.SH DESCRIPTION
+The object of the game
+.I hunt
+is to kill off the other players.
+There are no rooms, no treasures, and no monsters.
+Instead, you wander around a maze, find grenades, trip mines, and shoot down
+walls and players.
+The more players you kill before you die, the better your score is.
+If the
+.B \-m
+flag is given,
+you enter the game as a monitor
+(you can see the action but you cannot play).
+.PP
+.I Hunt
+normally looks for an active game on the local network;
+if none is found, it starts one up on the local host.
+The location of the game may be specified by giving the
+.I host
+argument.
+This presupposes that a hunt game is already running on that host, see
+.IR huntd (6)
+for details on how to setup a game on a specific host.
+If more than one game if found,
+you may pick which game to play in.
+.PP
+If the
+.B \-q
+flag is given,
+.I hunt
+queries the local network (or specific host)
+and reports on all active games found.
+This is useful for shell startup scripts, \fIe.g.\fP csh's .login.
+.PP
+The player name may be specified on the command line by using the
+.B \-n
+option.
+.PP
+The
+.BR \-c ,
+.BR \-s ,
+and
+.B \-f
+options are for entering the game cloaked, scanning, or flying respectively.
+.PP
+The
+.B \-b
+option turns off beeping when you reach the typeahead limit.
+.PP
+The
+.B \-t
+option aids team playing by making everyone else on one's team
+appear as the team name.
+A team name is a single digit to avoid conflicting with other characters
+used in the game.
+.PP
+The
+.B \-p
+.I port
+option allows the rendezvous port number to be set.
+This is a useful way for people playing on dialup lines to avoid playing
+with people on 9600 baud terminals.
+.PP
+The
+.B \-w
+.I message
+option is the only way to send a message to everyone else's screen when
+you start up.
+It is most often used to say ``eat slime death - NickD's coming in''.
+.PP
+When you die and are asked if you wish to re-enter the game,
+there are other answers than just yes or no.
+You can also reply with a
+.B w
+for write a message before continuing or
+.B o
+to change how you enter the game (cloaked, scanning, or flying).
+.PP
+To be notified automatically when a
+.I hunt
+starts up, add your login to the
+.I hunt-players
+mailing list (see
+.IR huntd (6)).
+.SH "PLAYING HINTS"
+.I Hunt
+only works on crt (vdt) terminals with at least 24 lines, 80 columns, and
+cursor addressing.
+The screen is divided in to 3 areas.
+On the right hand side is the status area.
+It shows damage sustained,
+charges remaining,
+who's in the game,
+who's scanning (the
+.B ``*''
+in front of the name),
+who's cloaked (the
+.B ``+''
+in front of the name),
+and other players' scores.
+The rest of the screen is taken up by your map of the maze.
+The 24th line
+is used for longer messages that don't fit in the status area.
+.PP
+.I Hunt
+uses the same keys to move as
+.IR vi (1)
+does,
+.IR i.e. ,
+.BR h ,
+.BR j ,
+.BR k ,
+and
+.B l
+for left, down, up, right respectively.
+To change which direction you're facing in the maze,
+use the upper case version of the movement key (\c
+.IR i.e. ,
+.BR HJKL ).
+You can only fire or throw things in the direction you're facing.
+.TP
+Other commands are:
+.sp
+.nf
+.ta
+.ta \w'>\|<\|^\|v\ \ 'u
+f or 1 \- Fire a bullet (Takes 1 charge)
+g or 2 \- Throw grenade (Takes 9 charges)
+F or 3 \- Throw satchel charge (Takes 25 charges)
+G or 4 \- Throw bomb (Takes 49 charges)
+5 \- Throw big bomb (Takes 81 charges)
+6 \- Throw even bigger bomb (Takes 121 charges)
+7 \- Throw even more big bomb (Takes 169 charges)
+8 \- Throw even more bigger bomb (Takes 225 charges)
+9 \- Throw very big bomb (Takes 289 charges)
+0 \- Throw very, very big bomb (Takes 361 charges)
+@ \- Throw biggest bomb (Takes 441 charges)
+o \- Throw small slime (Takes 15 charges)
+O \- Throw big slime (Takes 30 charges)
+p \- Throw bigger slime (Takes 45 charges)
+P \- Throw biggest slime (Takes 60 charges)
+s \- Scan (show where other players are) (Takes 1 charge)
+c \- Cloak (hide from scanners) (Takes 1 charge)
+
+^L \- Redraw screen
+q \- Quit
+.fi
+.TP
+The symbols on the screen are:
+.sp
+.nf
+.ta
+.ta \w'>\|<\|^\|v\ \ 'u
+\-\||\|+ \- walls
+/\|\\ \- diagonal (deflecting) walls
+# \- doors (dispersion walls)
+; \- small mine
+g \- large mine
+: \- bullet
+o \- grenade
+O \- satchel charge
+@ \- bomb
+s \- small slime
+$ \- big slime
+>\|<\|^\|v \- you facing right, left, up, or down
+}\|{\|i\|! \- other players facing right, left, up, or down
+\(** \- explosion
+.ne 3
+.cs R 24
+.cs I 24
+\fR\\|/\fP
+.cs R
+\fI\-\(**\-\fP \- grenade and large mine explosion
+.fl
+.cs R 24
+\fR/|\\\fP
+.cs R
+.cs I
+.fi
+.LP
+Other helpful hints:
+.sp
+.ie n .ds b []
+.el .ds b \(bu
+.ta
+.ta \w'\*b\ \|'u
+.nr In \n(.i
+.de MP
+.br
+.in \n(Inu+\w'\*b\ \|'u
+.ti \n(Inu
+\*b \c
+..
+.MP
+You can only fire in the direction you are facing.
+.MP
+You can only fire three shots in a row, then the gun must cool off.
+.MP
+Shots move 5 times faster than you do.
+.MP
+To stab someone,
+you face that player and move at them.
+.MP
+Stabbing does 2 points worth of damage and shooting does 5 points.
+.MP
+Slime does 5 points of damage each time it hits.
+.MP
+You start with 15 charges and get 5 more every time a player enters
+or re-enters.
+.MP
+Grenade explosions cover a 3 by 3 area, each larger bomb cover a
+correspondingly larger area (ranging from 5 by 5 to 21 by 21).
+All explosions are centered around the square the shot hits and
+do the most damage in the center.
+.MP
+Slime affects all squares it oozes over.
+The number of squares is equal to the number of charges used.
+.MP
+One small mine and one large mine is placed in the maze for every new player.
+A mine has a 2% probability of tripping when you walk forward on to it;
+50% when going sideways;
+95% when backing up.
+Tripping a mine costs you 5 points or 10 points respectively.
+Defusing a mine is worth 1 charge or 9 charges respectively.
+.MP
+You cannot see behind you.
+.MP
+Cloaking consumes 1 ammo charge per 20 of your moves.
+.MP
+Scanning consumes 1 ammo charge per (20 \(mu the number of players)
+of other player moves.
+.MP
+Turning on cloaking turns off scanning \(em turning on scanning turns off
+cloaking.
+.MP
+When you kill someone,
+you get 2 more damage capacity points and 2 damage points get taken away.
+.MP
+Maximum typeahead is 5 characters.
+.MP
+A shot destroys normal (\c
+.IR i.e.,
+non-diagonal, non-door) walls.
+.MP
+Diagonal walls deflect shots and change orientation.
+.MP
+Doors disperse shots in random directions (up, down, left, right).
+.MP
+Diagonal walls and doors cannot be destroyed by direct shots but may
+be destroyed by an adjacent grenade explosion.
+.MP
+Slime goes around walls, not through them.
+.MP
+Walls regenerate, reappearing in the order they were destroyed.
+One percent of the regenerated walls will be diagonal walls or doors.
+When a wall is generated directly beneath a player, he is thrown in
+a random direction for a random period of time. When he lands, he
+sustains damage (up to 20 percent of the amount of damage already
+sustained);
+.IR i.e. ,
+the less damage he had, the more nimble he is and
+therefore less likely to hurt himself on landing.
+.\"MP
+.\"There is a volcano close to the center of the maze which goes off
+.\"close to every 30 deaths.
+.MP
+Every 30 deaths or so, a
+.B ``?''
+will appear.
+It is a wandering bomb which will explode when it hits someone, or
+when it is slimed.
+.MP
+If no one moves, everything stands still.
+.MP
+The environment variable
+.B HUNT
+is checked to get the player name.
+If you don't have this variable set,
+.I hunt
+will ask you what name you want to play under.
+If you wish to set other options than just your name,
+you can enumerate the options as follows:
+.br
+.ti +1i
+setenv HUNT "name=Sneaky,team=1,cloak,mapkey=zoFfGg1f2g3F4G"
+.br
+sets the player name to Sneaky,
+sets the team to one,
+sets the enter game attribute to cloaked,
+and the maps \fBz\fP to \fBo\fP, \fBF\fP to \fBf\fP, \fBG\fP to \fBg\fP,
+\fB1\fP to \fBf\fP,
+\fB2\fP to \fBg\fP, \fB3\fP to \fBF\fP, and \fB4\fP to \fBG\fP.
+The \fImapkey\fP option must be last.
+Other options are: scan, fly, nobeep, port=string, host=string,
+and message=string \(em which correspond to the command line options.
+String options cannot contain commas since commas
+are used to separate options.
+.MP
+It's a boring game if you're the only one playing.
+.PP
+Your score is the decayed average of the ratio of number of kills to number
+of times you entered the game and is only kept for the duration
+of a single session of \fIhunt\fP.
+.PP
+.I Hunt
+normally drives up the load average to be approximately
+(number_of_players + 0.5) greater than it would be without a
+.I hunt
+game executing.
+.SH STATISTICS
+The
+.B \-S
+option fetches the current game statistics.
+The meaning of the column headings are as follows:
+.I score
+\(em the player's last score;
+.I ducked
+\(em
+how many shots a player ducked;
+.I absorb
+\(em how many shots a player absorbed;
+.I faced
+\(em how many shots were fired at player's face;
+.I shot
+\(em how many shots were fired at player;
+.I robbed
+\(em how many of player's shots were absorbed;
+.I missed
+\(em how many of player's shots were ducked;
+.I slimeK
+\(em how many slime kills player had;
+.I enemy
+\(em how many enemies were killed;
+.I friend
+\(em how many friends were killed (self and same team);
+.I deaths
+\(em how many times player died;
+.I still
+\(em how many times player died without typing in any commands;
+.I saved
+\(em how many times a shot/bomb would have killed player if he hadn't
+ducked or absorbed it.
+.SH FILES
+.nf
+.ta
+.ta \w'/usr/games/lib/huntd\ \ \ 'u
+/usr/games/lib/huntd game coordinator
+.DT
+.fi
+.SH "SEE ALSO"
+huntd(6)
+.SH AUTHORS
+Conrad Huang, Ken Arnold, and Greg Couch;
+.br
+University of California, San Francisco, Computer Graphics Lab
+.SH ACKNOWLEDGEMENTS
+We thank Don Kneller,
+John Thomason, Eric Pettersen, Mark Day,
+and Scott Weiner for providing
+endless hours of play-testing to improve the character of the game.
+We hope their significant others will forgive them;
+we certainly don't.
+.SH BUGS
+To keep up the pace, not everything is as realistic as possible.
diff --git a/hunt/hunt/hunt.c b/hunt/hunt/hunt.c
new file mode 100644
index 00000000..6599b87e
--- /dev/null
+++ b/hunt/hunt/hunt.c
@@ -0,0 +1,1072 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include <errno.h>
+# if !defined(USE_CURSES) && defined(BSD_RELEASE) && BSD_RELEASE >= 44
+# include <termios.h>
+static struct termios saved_tty;
+# endif
+# include <curses.h>
+# include <string.h>
+# include "hunt.h"
+# include <signal.h>
+# include <ctype.h>
+# include <sys/stat.h>
+# include <sys/time.h>
+
+/*
+ * Some old versions of curses don't have these defined
+ */
+# if !defined(cbreak) && (!defined(BSD_RELEASE) || BSD_RELEASE < 44)
+# define cbreak() crmode()
+# endif
+
+# if !defined(USE_CURSES) || !defined(TERMINFO)
+# define beep() (void) putchar(CTRL('G'))
+# endif
+# if !defined(USE_CURSES)
+# undef refresh
+# define refresh() (void) fflush(stdout);
+# endif
+# ifdef USE_CURSES
+# define clear_eol() clrtoeol()
+# define put_ch addch
+# define put_str addstr
+# endif
+
+#if !defined(BSD_RELEASE) || BSD_RELEASE < 44
+extern int _putchar();
+#endif
+
+FLAG Last_player = FALSE;
+# ifdef MONITOR
+FLAG Am_monitor = FALSE;
+# endif
+
+char Buf[BUFSIZ];
+
+int Socket;
+# ifdef INTERNET
+char *Sock_host;
+char *use_port;
+FLAG Query_driver = FALSE;
+char *Send_message = NULL;
+FLAG Show_scores = FALSE;
+# endif
+
+SOCKET Daemon;
+# ifdef INTERNET
+# define DAEMON_SIZE (sizeof Daemon)
+# else
+# define DAEMON_SIZE (sizeof Daemon - 1)
+# endif
+
+char map_key[256]; /* what to map keys to */
+FLAG no_beep;
+
+static char name[NAMELEN];
+static char team = ' ';
+
+static int in_visual;
+
+extern int cur_row, cur_col;
+extern char *tgoto();
+
+# ifdef INTERNET
+extern SOCKET *list_drivers();
+# endif
+
+/*
+ * main:
+ * Main program for local process
+ */
+main(ac, av)
+int ac;
+char **av;
+{
+ char *term;
+ int c;
+ extern int errno;
+ extern int Otto_mode;
+ extern int optind;
+ extern char *optarg;
+ long enter_status;
+ SIGNAL_TYPE intr(), sigterm(), sigemt(), tstp();
+ long env_init(), quit();
+
+ enter_status = env_init((long) Q_CLOAK);
+ while ((c = getopt(ac, av, "Sbcfh:l:mn:op:qst:w:")) != EOF) {
+ switch (c) {
+ case 'l': /* rsh compatibility */
+ case 'n':
+ (void) strncpy(name, optarg, NAMELEN);
+ break;
+ case 't':
+ team = *optarg;
+ if (!isdigit(team)) {
+ fprintf(stderr, "Team names must be numeric\n");
+ team = ' ';
+ }
+ break;
+ case 'o':
+# ifndef OTTO
+ fputs("The -o flag is reserved for future use.\n",
+ stderr);
+ goto usage;
+# else
+ Otto_mode = TRUE;
+ break;
+# endif
+ case 'm':
+# ifdef MONITOR
+ Am_monitor = TRUE;
+# else
+ fputs("The monitor was not compiled in.\n", stderr);
+# endif
+ break;
+# ifdef INTERNET
+ case 'S':
+ Show_scores = TRUE;
+ break;
+ case 'q': /* query whether hunt is running */
+ Query_driver = TRUE;
+ break;
+ case 'w':
+ Send_message = optarg;
+ break;
+ case 'h':
+ Sock_host = optarg;
+ break;
+ case 'p':
+ use_port = optarg;
+ Test_port = atoi(use_port);
+ break;
+# else
+ case 'S':
+ case 'q':
+ case 'w':
+ case 'h':
+ case 'p':
+ fputs("Need TCP/IP for S, q, w, h, and p options.\n",
+ stderr);
+ break;
+# endif
+ case 'c':
+ enter_status = Q_CLOAK;
+ break;
+ case 'f':
+# ifdef FLY
+ enter_status = Q_FLY;
+# else
+ fputs("The flying code was not compiled in.\n", stderr);
+# endif
+ break;
+ case 's':
+ enter_status = Q_SCAN;
+ break;
+ case 'b':
+ no_beep = !no_beep;
+ break;
+ default:
+ usage:
+ fputs(
+"usage:\thunt [-qmcsfS] [-n name] [-t team] [-p port] [-w message] [host]\n",
+ stderr);
+ exit(1);
+ }
+ }
+# ifdef INTERNET
+ if (optind + 1 < ac)
+ goto usage;
+ else if (optind + 1 == ac)
+ Sock_host = av[ac - 1];
+# else
+ if (optind > ac)
+ goto usage;
+# endif
+
+# ifdef INTERNET
+ if (Show_scores) {
+ SOCKET *hosts;
+
+ for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1)
+ dump_scores(*hosts);
+ exit(0);
+ }
+ if (Query_driver) {
+ SOCKET *hosts;
+
+ for (hosts = list_drivers(); hosts->sin_port != 0; hosts += 1) {
+ struct hostent *hp;
+ int num_players;
+
+ hp = gethostbyaddr((char *) &hosts->sin_addr,
+ sizeof hosts->sin_addr, AF_INET);
+ num_players = ntohs(hosts->sin_port);
+ printf("%d player%s hunting on %s!\n",
+ num_players, (num_players == 1) ? "" : "s",
+ hp != NULL ? hp->h_name :
+ inet_ntoa(hosts->sin_addr));
+ }
+ exit(0);
+ }
+# endif
+# ifdef OTTO
+ if (Otto_mode)
+ (void) strncpy(name, "otto", NAMELEN);
+ else
+# endif
+ fill_in_blanks();
+
+ (void) fflush(stdout);
+ if (!isatty(0) || (term = getenv("TERM")) == NULL) {
+ fprintf(stderr, "no terminal type\n");
+ exit(1);
+ }
+# ifdef USE_CURSES
+ initscr();
+ (void) noecho();
+ (void) cbreak();
+# else /* !USE_CURSES */
+# if !defined(BSD_RELEASE) || BSD_RELEASE < 44
+ _tty_ch = 0;
+# endif
+ gettmode();
+ (void) setterm(term);
+ (void) noecho();
+ (void) cbreak();
+# if defined(BSD_RELEASE) && BSD_RELEASE >= 44
+ tcgetattr(0, &saved_tty);
+# endif
+ _puts(TI);
+ _puts(VS);
+# endif /* !USE_CURSES */
+ in_visual = TRUE;
+ if (LINES < SCREEN_HEIGHT || COLS < SCREEN_WIDTH)
+ leave(1, "Need a larger window");
+ clear_the_screen();
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGTERM, sigterm);
+ (void) signal(SIGEMT, sigemt);
+ (void) signal(SIGPIPE, SIG_IGN);
+#if !defined(USE_CURSES) && defined(SIGTSTP)
+ (void) signal(SIGTSTP, tstp);
+#endif
+
+ for (;;) {
+# ifdef INTERNET
+ find_driver(TRUE);
+
+ if (Daemon.sin_port == 0)
+ leave(1, "Game not found, try again");
+
+ jump_in:
+ do {
+ int option;
+
+ Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
+ if (Socket < 0) {
+ perror("socket");
+ exit(1);
+ }
+ option = 1;
+ if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK,
+ &option, sizeof option) < 0)
+ perror("setsockopt loopback");
+ errno = 0;
+ if (connect(Socket, (struct sockaddr *) &Daemon,
+ DAEMON_SIZE) < 0) {
+ if (errno != ECONNREFUSED) {
+ perror("connect");
+ leave(1, "connect");
+ }
+ }
+ else
+ break;
+ sleep(1);
+ } while (close(Socket) == 0);
+# else /* !INTERNET */
+ /*
+ * set up a socket
+ */
+
+ if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+
+ /*
+ * attempt to connect the socket to a name; if it fails that
+ * usually means that the driver isn't running, so we start
+ * up the driver.
+ */
+
+ Daemon.sun_family = SOCK_FAMILY;
+ (void) strcpy(Daemon.sun_path, Sock_name);
+ if (connect(Socket, &Daemon, DAEMON_SIZE) < 0) {
+ if (errno != ENOENT) {
+ perror("connect");
+ leave(1, "connect2");
+ }
+ start_driver();
+
+ do {
+ (void) close(Socket);
+ if ((Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+ sleep(2);
+ } while (connect(Socket, &Daemon, DAEMON_SIZE) < 0);
+ }
+# endif
+
+ do_connect(name, team, enter_status);
+# ifdef INTERNET
+ if (Send_message != NULL) {
+ do_message();
+ if (enter_status == Q_MESSAGE)
+ break;
+ Send_message = NULL;
+ /* don't continue as that will call find_driver */
+ goto jump_in;
+ }
+# endif
+ playit();
+ if ((enter_status = quit(enter_status)) == Q_QUIT)
+ break;
+ }
+ leave(0, (char *) NULL);
+ /* NOTREACHED */
+}
+
+# ifdef INTERNET
+# ifdef BROADCAST
+broadcast_vec(s, vector)
+ int s; /* socket */
+ struct sockaddr **vector;
+{
+ char if_buf[BUFSIZ];
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ unsigned int n;
+ int vec_cnt;
+
+ *vector = NULL;
+ ifc.ifc_len = sizeof if_buf;
+ ifc.ifc_buf = if_buf;
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
+ return 0;
+ vec_cnt = 0;
+ n = ifc.ifc_len / sizeof (struct ifreq);
+ *vector = (struct sockaddr *) malloc(n * sizeof (struct sockaddr));
+ for (ifr = ifc.ifc_req; n != 0; n--, ifr++)
+ if (ioctl(s, SIOCGIFBRDADDR, ifr) >= 0)
+ memcpy(&(*vector)[vec_cnt++], &ifr->ifr_addr,
+ sizeof (struct sockaddr));
+ return vec_cnt;
+}
+# endif
+
+SOCKET *
+list_drivers()
+{
+ int option;
+ u_short msg;
+ u_short port_num;
+ static SOCKET test;
+ int test_socket;
+ int namelen;
+ char local_name[256];
+ static initial = TRUE;
+ static struct in_addr local_address;
+ register struct hostent *hp;
+ extern int errno;
+# ifdef BROADCAST
+ static int brdc;
+ static SOCKET *brdv;
+# else
+ u_long local_net;
+# endif
+ int i;
+ static SOCKET *listv;
+ static unsigned int listmax;
+ unsigned int listc;
+ int mask;
+ struct timeval wait;
+
+ if (initial) { /* do one time initialization */
+# ifndef BROADCAST
+ sethostent(1); /* don't bother to close host file */
+# endif
+ if (gethostname(local_name, sizeof local_name) < 0) {
+ leave(1, "Sorry, I have no name.");
+ /* NOTREACHED */
+ }
+ if ((hp = gethostbyname(local_name)) == NULL) {
+ leave(1, "Can't find myself.");
+ /* NOTREACHED */
+ }
+ local_address = * ((struct in_addr *) hp->h_addr);
+
+ listmax = 20;
+ listv = (SOCKET *) malloc(listmax * sizeof (SOCKET));
+ } else if (Sock_host != NULL)
+ return listv; /* address already valid */
+
+ test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
+ if (test_socket < 0) {
+ perror("socket");
+ leave(1, "socket system call failed");
+ /* NOTREACHED */
+ }
+ test.sin_family = SOCK_FAMILY;
+ test.sin_port = htons(Test_port);
+ listc = 0;
+
+ if (Sock_host != NULL) { /* explicit host given */
+ if ((hp = gethostbyname(Sock_host)) == NULL) {
+ leave(1, "Unknown host");
+ /* NOTREACHED */
+ }
+ test.sin_addr = *((struct in_addr *) hp->h_addr);
+ goto test_one_host;
+ }
+
+ if (!initial) {
+ /* favor host of previous session by broadcasting to it first */
+ test.sin_addr = Daemon.sin_addr;
+ msg = htons(C_PLAYER); /* Must be playing! */
+ (void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
+ (struct sockaddr *) &test, DAEMON_SIZE);
+ }
+
+# ifdef BROADCAST
+ if (initial)
+ brdc = broadcast_vec(test_socket, (struct sockaddr **) &brdv);
+
+ if (brdc <= 0) {
+ initial = FALSE;
+ test.sin_addr = local_address;
+ goto test_one_host;
+ }
+
+# ifdef SO_BROADCAST
+ /* Sun's will broadcast even though this option can't be set */
+ option = 1;
+ if (setsockopt(test_socket, SOL_SOCKET, SO_BROADCAST,
+ &option, sizeof option) < 0) {
+ perror("setsockopt broadcast");
+ leave(1, "setsockopt broadcast");
+ /* NOTREACHED */
+ }
+# endif
+
+ /* send broadcast packets on all interfaces */
+ msg = htons(C_TESTMSG());
+ for (i = 0; i < brdc; i++) {
+ test.sin_addr = brdv[i].sin_addr;
+ if (sendto(test_socket, (char *) &msg, sizeof msg, 0,
+ (struct sockaddr *) &test, DAEMON_SIZE) < 0) {
+ perror("sendto");
+ leave(1, "sendto");
+ /* NOTREACHED */
+ }
+ }
+# else /* !BROADCAST */
+ /* loop thru all hosts on local net and send msg to them. */
+ msg = htons(C_TESTMSG());
+ local_net = inet_netof(local_address);
+ sethostent(0); /* rewind host file */
+ while (hp = gethostent()) {
+ if (local_net == inet_netof(* ((struct in_addr *) hp->h_addr))){
+ test.sin_addr = * ((struct in_addr *) hp->h_addr);
+ (void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
+ (struct sockaddr *) &test, DAEMON_SIZE);
+ }
+ }
+# endif
+
+get_response:
+ namelen = DAEMON_SIZE;
+ errno = 0;
+ wait.tv_sec = 1;
+ wait.tv_usec = 0;
+ for (;;) {
+ if (listc + 1 >= listmax) {
+ listmax += 20;
+ listv = (SOCKET *) realloc((char *) listv,
+ listmax * sizeof(SOCKET));
+ }
+
+ mask = 1 << test_socket;
+ if (select(test_socket + 1, &mask, NULL, NULL, &wait) == 1
+ && recvfrom(test_socket, (char *) &port_num, sizeof port_num,
+ 0, (struct sockaddr *) &listv[listc], &namelen) > 0) {
+ /*
+ * Note that we do *not* convert from network to host
+ * order since the port number *should* be in network
+ * order:
+ */
+ for (i = 0; i < listc; i += 1)
+ if (listv[listc].sin_addr.s_addr
+ == listv[i].sin_addr.s_addr)
+ break;
+ if (i == listc)
+ listv[listc++].sin_port = port_num;
+ continue;
+ }
+
+ if (errno != 0 && errno != EINTR) {
+ perror("select/recvfrom");
+ leave(1, "select/recvfrom");
+ /* NOTREACHED */
+ }
+
+ /* terminate list with local address */
+ listv[listc].sin_family = SOCK_FAMILY;
+ listv[listc].sin_addr = local_address;
+ listv[listc].sin_port = htons(0);
+
+ (void) close(test_socket);
+ initial = FALSE;
+ return listv;
+ }
+
+test_one_host:
+ msg = htons(C_TESTMSG());
+ (void) sendto(test_socket, (char *) &msg, sizeof msg, 0,
+ (struct sockaddr *) &test, DAEMON_SIZE);
+ goto get_response;
+}
+
+find_driver(do_startup)
+FLAG do_startup;
+{
+ SOCKET *hosts;
+
+ hosts = list_drivers();
+ if (hosts[0].sin_port != htons(0)) {
+ int i, c;
+
+ if (hosts[1].sin_port == htons(0)) {
+ Daemon = hosts[0];
+ return;
+ }
+ /* go thru list and return host that matches daemon */
+ clear_the_screen();
+# ifdef USE_CURSES
+ move(1, 0);
+# else
+ mvcur(cur_row, cur_col, 1, 0);
+ cur_row = 1;
+ cur_col = 0;
+# endif
+ put_str("Pick one:");
+ for (i = 0; i < HEIGHT - 4 && hosts[i].sin_port != htons(0);
+ i += 1) {
+ struct hostent *hp;
+ char buf[80];
+
+# ifdef USE_CURSES
+ move(3 + i, 0);
+# else
+ mvcur(cur_row, cur_col, 3 + i, 0);
+ cur_row = 3 + i;
+ cur_col = 0;
+# endif
+ hp = gethostbyaddr((char *) &hosts[i].sin_addr,
+ sizeof hosts[i].sin_addr, AF_INET);
+ (void) sprintf(buf, "%8c %.64s", 'a' + i,
+ hp != NULL ? hp->h_name
+ : inet_ntoa(hosts->sin_addr));
+ put_str(buf);
+ }
+# ifdef USE_CURSES
+ move(4 + i, 0);
+# else
+ mvcur(cur_row, cur_col, 4 + i, 0);
+ cur_row = 4 + i;
+ cur_col = 0;
+# endif
+ put_str("Enter letter: ");
+ refresh();
+ while (!islower(c = getchar()) || (c -= 'a') >= i) {
+ beep();
+ refresh();
+ }
+ Daemon = hosts[c];
+ clear_the_screen();
+ return;
+ }
+ if (!do_startup)
+ return;
+
+ start_driver();
+ sleep(2);
+ find_driver(FALSE);
+}
+
+dump_scores(host)
+ SOCKET host;
+{
+ struct hostent *hp;
+ int s;
+ char buf[BUFSIZ];
+ int cnt;
+
+ hp = gethostbyaddr((char *) &host.sin_addr, sizeof host.sin_addr,
+ AF_INET);
+ printf("\n%s:\n", hp != NULL ? hp->h_name : inet_ntoa(host.sin_addr));
+ fflush(stdout);
+
+ s = socket(SOCK_FAMILY, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("socket");
+ exit(1);
+ }
+ if (connect(s, (struct sockaddr *) &host, sizeof host) < 0) {
+ perror("connect");
+ exit(1);
+ }
+ while ((cnt = read(s, buf, BUFSIZ)) > 0)
+ write(fileno(stdout), buf, cnt);
+ (void) close(s);
+}
+
+# endif
+
+start_driver()
+{
+ register int procid;
+
+# ifdef MONITOR
+ if (Am_monitor) {
+ leave(1, "No one playing.");
+ /* NOTREACHED */
+ }
+# endif
+
+# ifdef INTERNET
+ if (Sock_host != NULL) {
+ sleep(3);
+ return;
+ }
+# endif
+
+# ifdef USE_CURSES
+ move(HEIGHT, 0);
+# else
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ cur_row = HEIGHT;
+ cur_col = 0;
+# endif
+ put_str("Starting...");
+ refresh();
+ procid = fork();
+ if (procid == -1) {
+ perror("fork");
+ leave(1, "fork failed.");
+ }
+ if (procid == 0) {
+ (void) signal(SIGINT, SIG_IGN);
+# ifndef INTERNET
+ (void) close(Socket);
+# else
+ if (use_port == NULL)
+# endif
+ execl(Driver, "HUNT", (char *) NULL);
+# ifdef INTERNET
+ else
+ execl(Driver, "HUNT", "-p", use_port, (char *) NULL);
+# endif
+ /* only get here if exec failed */
+ (void) kill(getppid(), SIGEMT); /* tell mom */
+ _exit(1);
+ }
+# ifdef USE_CURSES
+ move(HEIGHT, 0);
+# else
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ cur_row = HEIGHT;
+ cur_col = 0;
+# endif
+ put_str("Connecting...");
+ refresh();
+}
+
+/*
+ * bad_con:
+ * We had a bad connection. For the moment we assume that this
+ * means the game is full.
+ */
+bad_con()
+{
+ leave(1, "The game is full. Sorry.");
+ /* NOTREACHED */
+}
+
+/*
+ * bad_ver:
+ * version number mismatch.
+ */
+bad_ver()
+{
+ leave(1, "Version number mismatch. No go.");
+ /* NOTREACHED */
+}
+
+/*
+ * sigterm:
+ * Handle a terminate signal
+ */
+SIGNAL_TYPE
+sigterm()
+{
+ leave(0, (char *) NULL);
+ /* NOTREACHED */
+}
+
+
+/*
+ * sigemt:
+ * Handle a emt signal - shouldn't happen on vaxes(?)
+ */
+SIGNAL_TYPE
+sigemt()
+{
+ leave(1, "Unable to start driver. Try again.");
+ /* NOTREACHED */
+}
+
+# ifdef INTERNET
+/*
+ * sigalrm:
+ * Handle an alarm signal
+ */
+SIGNAL_TYPE
+sigalrm()
+{
+ return;
+}
+# endif
+
+/*
+ * rmnl:
+ * Remove a '\n' at the end of a string if there is one
+ */
+rmnl(s)
+char *s;
+{
+ register char *cp;
+
+ cp = strrchr(s, '\n');
+ if (cp != NULL)
+ *cp = '\0';
+}
+
+/*
+ * intr:
+ * Handle a interrupt signal
+ */
+SIGNAL_TYPE
+intr()
+{
+ register int ch;
+ register int explained;
+ register int y, x;
+
+ (void) signal(SIGINT, SIG_IGN);
+# ifdef USE_CURSES
+ getyx(stdscr, y, x);
+ move(HEIGHT, 0);
+# else
+ y = cur_row;
+ x = cur_col;
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ cur_row = HEIGHT;
+ cur_col = 0;
+# endif
+ put_str("Really quit? ");
+ clear_eol();
+ refresh();
+ explained = FALSE;
+ for (;;) {
+ ch = getchar();
+ if (isupper(ch))
+ ch = tolower(ch);
+ if (ch == 'y') {
+ if (Socket != 0) {
+ (void) write(Socket, "q", 1);
+ (void) close(Socket);
+ }
+ leave(0, (char *) NULL);
+ }
+ else if (ch == 'n') {
+ (void) signal(SIGINT, intr);
+# ifdef USE_CURSES
+ move(y, x);
+# else
+ mvcur(cur_row, cur_col, y, x);
+ cur_row = y;
+ cur_col = x;
+# endif
+ refresh();
+ return;
+ }
+ if (!explained) {
+ put_str("(Yes or No) ");
+ refresh();
+ explained = TRUE;
+ }
+ beep();
+ refresh();
+ }
+}
+
+/*
+ * leave:
+ * Leave the game somewhat gracefully, restoring all current
+ * tty stats.
+ */
+leave(eval, mesg)
+int eval;
+char *mesg;
+{
+ if (in_visual) {
+# ifdef USE_CURSES
+ move(HEIGHT, 0);
+ refresh();
+ endwin();
+# else /* !USE_CURSES */
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ (void) fflush(stdout); /* flush in case VE changes pages */
+# if defined(BSD_RELEASE) && BSD_RELEASE >= 44
+ tcsetattr(0, TCSADRAIN, &__orig_termios);
+# else
+ resetty();
+# endif
+ _puts(VE);
+ _puts(TE);
+# endif /* !USE_CURSES */
+ }
+ if (mesg != NULL)
+ puts(mesg);
+ exit(eval);
+}
+
+#if !defined(USE_CURSES) && defined(SIGTSTP)
+/*
+ * tstp:
+ * Handle stop and start signals
+ */
+SIGNAL_TYPE
+tstp()
+{
+# if BSD_RELEASE < 44
+ static struct sgttyb tty;
+# endif
+ int y, x;
+
+ y = cur_row;
+ x = cur_col;
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ cur_row = HEIGHT;
+ cur_col = 0;
+# if !defined(BSD_RELEASE) || BSD_RELEASE < 44
+ tty = _tty;
+# endif
+ _puts(VE);
+ _puts(TE);
+ (void) fflush(stdout);
+# if defined(BSD_RELEASE) && BSD_RELEASE >= 44
+ tcsetattr(0, TCSADRAIN, &__orig_termios);
+# else
+ resetty();
+# endif
+ (void) kill(getpid(), SIGSTOP);
+ (void) signal(SIGTSTP, tstp);
+# if defined(BSD_RELEASE) && BSD_RELEASE >= 44
+ tcsetattr(0, TCSADRAIN, &saved_tty);
+# else
+ _tty = tty;
+ ioctl(_tty_ch, TIOCSETP, &_tty);
+# endif
+ _puts(TI);
+ _puts(VS);
+ cur_row = y;
+ cur_col = x;
+ _puts(tgoto(CM, cur_row, cur_col));
+ redraw_screen();
+ (void) fflush(stdout);
+}
+#endif /* !defined(USE_CURSES) && defined(SIGTSTP) */
+
+# if defined(BSD_RELEASE) && BSD_RELEASE < 43
+char *
+strpbrk(s, brk)
+ register char *s, *brk;
+{
+ register char *p;
+ register c;
+
+ while (c = *s) {
+ for (p = brk; *p; p++)
+ if (c == *p)
+ return (s);
+ s++;
+ }
+ return (0);
+}
+# endif
+
+long
+env_init(enter_status)
+ long enter_status;
+{
+ register int i;
+ char *envp, *envname, *s;
+
+ for (i = 0; i < 256; i++)
+ map_key[i] = (char) i;
+
+ envname = NULL;
+ if ((envp = getenv("HUNT")) != NULL) {
+ while ((s = strpbrk(envp, "=,")) != NULL) {
+ if (strncmp(envp, "cloak,", s - envp + 1) == 0) {
+ enter_status = Q_CLOAK;
+ envp = s + 1;
+ }
+ else if (strncmp(envp, "scan,", s - envp + 1) == 0) {
+ enter_status = Q_SCAN;
+ envp = s + 1;
+ }
+ else if (strncmp(envp, "fly,", s - envp + 1) == 0) {
+ enter_status = Q_FLY;
+ envp = s + 1;
+ }
+ else if (strncmp(envp, "nobeep,", s - envp + 1) == 0) {
+ no_beep = TRUE;
+ envp = s + 1;
+ }
+ else if (strncmp(envp, "name=", s - envp + 1) == 0) {
+ envname = s + 1;
+ if ((s = strchr(envp, ',')) == NULL) {
+ *envp = '\0';
+ strncpy(name, envname, NAMELEN);
+ break;
+ }
+ *s = '\0';
+ strncpy(name, envname, NAMELEN);
+ envp = s + 1;
+ }
+# ifdef INTERNET
+ else if (strncmp(envp, "port=", s - envp + 1) == 0) {
+ use_port = s + 1;
+ Test_port = atoi(use_port);
+ if ((s = strchr(envp, ',')) == NULL) {
+ *envp = '\0';
+ break;
+ }
+ *s = '\0';
+ envp = s + 1;
+ }
+ else if (strncmp(envp, "host=", s - envp + 1) == 0) {
+ Sock_host = s + 1;
+ if ((s = strchr(envp, ',')) == NULL) {
+ *envp = '\0';
+ break;
+ }
+ *s = '\0';
+ envp = s + 1;
+ }
+ else if (strncmp(envp, "message=", s - envp + 1) == 0) {
+ Send_message = s + 1;
+ if ((s = strchr(envp, ',')) == NULL) {
+ *envp = '\0';
+ break;
+ }
+ *s = '\0';
+ envp = s + 1;
+ }
+# endif
+ else if (strncmp(envp, "team=", s - envp + 1) == 0) {
+ team = *(s + 1);
+ if (!isdigit(team))
+ team = ' ';
+ if ((s = strchr(envp, ',')) == NULL) {
+ *envp = '\0';
+ break;
+ }
+ *s = '\0';
+ envp = s + 1;
+ } /* must be last option */
+ else if (strncmp(envp, "mapkey=", s - envp + 1) == 0) {
+ for (s = s + 1; *s != '\0'; s += 2) {
+ map_key[(unsigned int) *s] = *(s + 1);
+ if (*(s + 1) == '\0') {
+ break;
+ }
+ }
+ *envp = '\0';
+ break;
+ } else {
+ *s = '\0';
+ printf("unknown option %s\n", envp);
+ if ((s = strchr(envp, ',')) == NULL) {
+ *envp = '\0';
+ break;
+ }
+ envp = s + 1;
+ }
+ }
+ if (*envp != '\0')
+ if (envname == NULL)
+ strncpy(name, envp, NAMELEN);
+ else
+ printf("unknown option %s\n", envp);
+ }
+ return enter_status;
+}
+
+fill_in_blanks()
+{
+ register int i;
+ register char *cp;
+
+again:
+ if (name[0] != '\0') {
+ printf("Entering as '%s'", name);
+ if (team != ' ')
+ printf(" on team %c.\n", team);
+ else
+ putchar('\n');
+ } else {
+ printf("Enter your code name: ");
+ if (fgets(name, NAMELEN, stdin) == NULL)
+ exit(1);
+ }
+ rmnl(name);
+ if (name[0] == '\0') {
+ name[0] = '\0';
+ printf("You have to have a code name!\n");
+ goto again;
+ }
+ for (cp = name; *cp != '\0'; cp++)
+ if (!isprint(*cp)) {
+ name[0] = '\0';
+ printf("Illegal character in your code name.\n");
+ goto again;
+ }
+ if (team == ' ') {
+ printf("Enter your team (0-9 or nothing): ");
+ i = getchar();
+ if (isdigit(i))
+ team = i;
+ while (i != '\n' && i != EOF)
+ i = getchar();
+ }
+}
diff --git a/hunt/hunt/otto.c b/hunt/hunt/otto.c
new file mode 100644
index 00000000..ab3b9029
--- /dev/null
+++ b/hunt/hunt/otto.c
@@ -0,0 +1,585 @@
+# ifdef OTTO
+/*
+ * otto - a hunt otto-matic player
+ *
+ * This guy is buggy, unfair, stupid, and not extensible.
+ * Future versions of hunt will have a subroutine library for
+ * automatic players to link to. If you write your own "otto"
+ * please let us know what subroutines you would expect in the
+ * subroutine library.
+ *
+ * $Id: otto.c,v 1.1.1.1 1997/10/04 09:00:14 mrg Exp $
+ */
+
+# include <curses.h>
+# include <ctype.h>
+# include "hunt.h"
+# include <sys/time.h>
+# include <signal.h>
+# undef WALL
+# undef NORTH
+# undef SOUTH
+# undef WEST
+# undef EAST
+# undef FRONT
+# undef LEFT
+# undef BACK
+# undef RIGHT
+
+# ifdef HPUX
+# define random rand
+# endif
+
+# ifndef USE_CURSES
+extern char screen[SCREEN_HEIGHT][SCREEN_WIDTH2];
+# define SCREEN(y, x) screen[y][x]
+# else
+# if defined(BSD_RELEASE) && BSD_RELEASE >= 44
+# define SCREEN(y, x) stdscr->lines[y]->line[x].ch
+# else
+# define SCREEN(y, x) stdscr->_y[y][x]
+# endif
+# endif
+
+# ifndef DEBUG
+# define STATIC static
+# else
+# define STATIC
+# endif
+
+# define OPPONENT "{}i!"
+# define PROPONENT "^v<>"
+# define WALL "+\\/#*-|"
+# define PUSHOVER " bg;*#&"
+# define SHOTS "$@Oo:"
+
+/* number of "directions" */
+# define NUMDIRECTIONS 4
+
+/* absolute directions (facings) - counterclockwise */
+# define NORTH 0
+# define WEST 1
+# define SOUTH 2
+# define EAST 3
+# define ALLDIRS 0xf
+
+/* relative directions - counterclockwise */
+# define FRONT 0
+# define LEFT 1
+# define BACK 2
+# define RIGHT 3
+
+# define ABSCHARS "NWSE"
+# define RELCHARS "FLBR"
+# define DIRKEYS "khjl"
+
+STATIC char command[BUFSIZ];
+STATIC int comlen;
+
+# ifdef DEBUG
+STATIC FILE *debug = NULL;
+# endif
+
+# define DEADEND 0x1
+# define ON_LEFT 0x2
+# define ON_RIGHT 0x4
+# define ON_SIDE (ON_LEFT|ON_RIGHT)
+# define BEEN 0x8
+# define BEEN_SAME 0x10
+
+struct item {
+ char what;
+ int distance;
+ int flags;
+};
+
+STATIC struct item flbr[NUMDIRECTIONS];
+
+# define fitem flbr[FRONT]
+# define litem flbr[LEFT]
+# define bitem flbr[BACK]
+# define ritem flbr[RIGHT]
+
+STATIC int facing;
+STATIC int row, col;
+STATIC int num_turns; /* for wandering */
+STATIC char been_there[HEIGHT][WIDTH2];
+STATIC struct itimerval pause_time = { { 0, 0 }, { 0, 55000 }};
+
+STATIC int stop_look();
+STATIC int look_around();
+STATIC int face_and_move_direction();
+STATIC int attack();
+STATIC int duck();
+STATIC int go_for_ammo();
+STATIC int wander();
+
+STATIC SIGNAL_TYPE
+nothing()
+{
+}
+
+otto(y, x, face)
+ int y, x;
+ char face;
+{
+ register int i;
+ extern int Otto_count;
+ int old_mask;
+
+# ifdef DEBUG
+ if (debug == NULL) {
+ debug = fopen("bug", "w");
+ setbuf(debug, NULL);
+ }
+ fprintf(debug, "\n%c(%d,%d)", face, y, x);
+# endif
+ (void) signal(SIGALRM, nothing);
+ old_mask = sigblock(sigmask(SIGALRM));
+ setitimer(ITIMER_REAL, &pause_time, NULL);
+ sigpause(old_mask);
+ sigsetmask(old_mask);
+
+ /* save away parameters so other functions may use/update info */
+ switch (face) {
+ case '^': facing = NORTH; break;
+ case '<': facing = WEST; break;
+ case 'v': facing = SOUTH; break;
+ case '>': facing = EAST; break;
+ default: abort();
+ }
+ row = y; col = x;
+ been_there[row][col] |= 1 << facing;
+
+ /* initially no commands to be sent */
+ comlen = 0;
+
+ /* find something to do */
+ look_around();
+ for (i = 0; i < NUMDIRECTIONS; i++) {
+ if (strchr(OPPONENT, flbr[i].what) != NULL) {
+ attack(i, &flbr[i]);
+ memset(been_there, 0, sizeof been_there);
+ goto done;
+ }
+ }
+
+ if (strchr(SHOTS, bitem.what) != NULL && !(bitem.what & ON_SIDE)) {
+ duck(BACK);
+ memset(been_there, 0, sizeof been_there);
+# ifdef BOOTS
+ } else if (go_for_ammo(BOOT_PAIR)) {
+ memset(been_there, 0, sizeof been_there);
+ } else if (go_for_ammo(BOOT)) {
+ memset(been_there, 0, sizeof been_there);
+# endif
+ } else if (go_for_ammo(GMINE))
+ memset(been_there, 0, sizeof been_there);
+ else if (go_for_ammo(MINE))
+ memset(been_there, 0, sizeof been_there);
+ else
+ wander();
+
+done:
+ (void) write(Socket, command, comlen);
+ Otto_count += comlen;
+# ifdef DEBUG
+ (void) fwrite(command, 1, comlen, debug);
+# endif
+}
+
+# define direction(abs,rel) (((abs) + (rel)) % NUMDIRECTIONS)
+
+STATIC
+stop_look(itemp, c, dist, side)
+ struct item *itemp;
+ char c;
+ int dist;
+ int side;
+{
+ switch (c) {
+
+ case SPACE:
+ if (side)
+ itemp->flags &= ~DEADEND;
+ return 0;
+
+ case MINE:
+ case GMINE:
+# ifdef BOOTS
+ case BOOT:
+ case BOOT_PAIR:
+# endif
+ if (itemp->distance == -1) {
+ itemp->distance = dist;
+ itemp->what = c;
+ if (side < 0)
+ itemp->flags |= ON_LEFT;
+ else if (side > 0)
+ itemp->flags |= ON_RIGHT;
+ }
+ return 0;
+
+ case SHOT:
+ case GRENADE:
+ case SATCHEL:
+ case BOMB:
+# ifdef OOZE
+ case SLIME:
+# endif
+ if (itemp->distance == -1 || (!side
+ && (itemp->flags & ON_SIDE
+ || itemp->what == GMINE || itemp->what == MINE))) {
+ itemp->distance = dist;
+ itemp->what = c;
+ itemp->flags &= ~ON_SIDE;
+ if (side < 0)
+ itemp->flags |= ON_LEFT;
+ else if (side > 0)
+ itemp->flags |= ON_RIGHT;
+ }
+ return 0;
+
+ case '{':
+ case '}':
+ case 'i':
+ case '!':
+ itemp->distance = dist;
+ itemp->what = c;
+ itemp->flags &= ~(ON_SIDE|DEADEND);
+ if (side < 0)
+ itemp->flags |= ON_LEFT;
+ else if (side > 0)
+ itemp->flags |= ON_RIGHT;
+ return 1;
+
+ default:
+ /* a wall or unknown object */
+ if (side)
+ return 0;
+ if (itemp->distance == -1) {
+ itemp->distance = dist;
+ itemp->what = c;
+ }
+ return 1;
+ }
+}
+
+look(rel_dir, itemp)
+ int rel_dir;
+ struct item *itemp;
+{
+ register int r, c;
+ register char ch;
+
+ itemp->what = 0;
+ itemp->distance = -1;
+ itemp->flags = DEADEND|BEEN; /* true until proven false */
+
+ switch (direction(facing, rel_dir)) {
+
+ case NORTH:
+ if (been_there[row - 1][col] & NORTH)
+ itemp->flags |= BEEN_SAME;
+ for (r = row - 1; r >= 0; r--)
+ for (c = col - 1; c < col + 2; c++) {
+ ch = SCREEN(r, c);
+ if (stop_look(itemp, ch, row - r, c - col))
+ goto cont_north;
+ if (c == col && !been_there[r][c])
+ itemp->flags &= ~BEEN;
+ }
+ cont_north:
+ if (itemp->flags & DEADEND) {
+ itemp->flags |= BEEN;
+ been_there[r][col] |= NORTH;
+ for (r = row - 1; r > row - itemp->distance; r--)
+ been_there[r][col] = ALLDIRS;
+ }
+ break;
+
+ case SOUTH:
+ if (been_there[row + 1][col] & SOUTH)
+ itemp->flags |= BEEN_SAME;
+ for (r = row + 1; r < HEIGHT; r++)
+ for (c = col - 1; c < col + 2; c++) {
+ ch = SCREEN(r, c);
+ if (stop_look(itemp, ch, r - row, col - c))
+ goto cont_south;
+ if (c == col && !been_there[r][c])
+ itemp->flags &= ~BEEN;
+ }
+ cont_south:
+ if (itemp->flags & DEADEND) {
+ itemp->flags |= BEEN;
+ been_there[r][col] |= SOUTH;
+ for (r = row + 1; r < row + itemp->distance; r++)
+ been_there[r][col] = ALLDIRS;
+ }
+ break;
+
+ case WEST:
+ if (been_there[row][col - 1] & WEST)
+ itemp->flags |= BEEN_SAME;
+ for (c = col - 1; c >= 0; c--)
+ for (r = row - 1; r < row + 2; r++) {
+ ch = SCREEN(r, c);
+ if (stop_look(itemp, ch, col - c, row - r))
+ goto cont_east;
+ if (r == row && !been_there[r][c])
+ itemp->flags &= ~BEEN;
+ }
+ cont_west:
+ if (itemp->flags & DEADEND) {
+ itemp->flags |= BEEN;
+ been_there[r][col] |= WEST;
+ for (c = col - 1; c > col - itemp->distance; c--)
+ been_there[row][c] = ALLDIRS;
+ }
+ break;
+
+ case EAST:
+ if (been_there[row][col + 1] & EAST)
+ itemp->flags |= BEEN_SAME;
+ for (c = col + 1; c < WIDTH; c++)
+ for (r = row - 1; r < row + 2; r++) {
+ ch = SCREEN(r, c);
+ if (stop_look(itemp, ch, c - col, r - row))
+ goto cont_east;
+ if (r == row && !been_there[r][c])
+ itemp->flags &= ~BEEN;
+ }
+ cont_east:
+ if (itemp->flags & DEADEND) {
+ itemp->flags |= BEEN;
+ been_there[r][col] |= EAST;
+ for (c = col + 1; c < col + itemp->distance; c++)
+ been_there[row][c] = ALLDIRS;
+ }
+ break;
+
+ default:
+ abort();
+ }
+}
+
+STATIC
+look_around()
+{
+ register int i;
+
+ for (i = 0; i < NUMDIRECTIONS; i++) {
+ look(i, &flbr[i]);
+# ifdef DEBUG
+ fprintf(debug, " look(%c)=%c(%d)(0x%x)",
+ RELCHARS[i], flbr[i].what, flbr[i].distance, flbr[i].flags);
+# endif
+ }
+}
+
+/*
+ * as a side effect modifies facing and location (row, col)
+ */
+
+STATIC
+face_and_move_direction(rel_dir, distance)
+ int rel_dir, distance;
+{
+ register int old_facing;
+ register char cmd;
+
+ old_facing = facing;
+ cmd = DIRKEYS[facing = direction(facing, rel_dir)];
+
+ if (rel_dir != FRONT) {
+ register int i;
+ struct item items[NUMDIRECTIONS];
+
+ command[comlen++] = toupper(cmd);
+ if (distance == 0) {
+ /* rotate look's to be in right position */
+ for (i = 0; i < NUMDIRECTIONS; i++)
+ items[i] =
+ flbr[(i + old_facing) % NUMDIRECTIONS];
+ memcpy(flbr, items, sizeof flbr);
+ }
+ }
+ while (distance--) {
+ command[comlen++] = cmd;
+ switch (facing) {
+
+ case NORTH: row--; break;
+ case WEST: col--; break;
+ case SOUTH: row++; break;
+ case EAST: col++; break;
+ }
+ if (distance == 0)
+ look_around();
+ }
+}
+
+STATIC
+attack(rel_dir, itemp)
+ int rel_dir;
+ struct item *itemp;
+{
+ if (!(itemp->flags & ON_SIDE)) {
+ face_and_move_direction(rel_dir, 0);
+ command[comlen++] = 'o';
+ command[comlen++] = 'o';
+ duck(FRONT);
+ command[comlen++] = ' ';
+ } else if (itemp->distance > 1) {
+ face_and_move_direction(rel_dir, 2);
+ duck(FRONT);
+ } else {
+ face_and_move_direction(rel_dir, 1);
+ if (itemp->flags & ON_LEFT)
+ rel_dir = LEFT;
+ else
+ rel_dir = RIGHT;
+ (void) face_and_move_direction(rel_dir, 0);
+ command[comlen++] = 'f';
+ command[comlen++] = 'f';
+ duck(FRONT);
+ command[comlen++] = ' ';
+ }
+}
+
+STATIC
+duck(rel_dir)
+ int rel_dir;
+{
+ int dir;
+
+ switch (dir = direction(facing, rel_dir)) {
+
+ case NORTH:
+ case SOUTH:
+ if (strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
+ command[comlen++] = 'h';
+ else if (strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
+ command[comlen++] = 'l';
+ else if (dir == NORTH
+ && strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
+ command[comlen++] = 'j';
+ else if (dir == SOUTH
+ && strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
+ command[comlen++] = 'k';
+ else if (dir == NORTH)
+ command[comlen++] = 'k';
+ else
+ command[comlen++] = 'j';
+ break;
+
+ case WEST:
+ case EAST:
+ if (strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
+ command[comlen++] = 'k';
+ else if (strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
+ command[comlen++] = 'j';
+ else if (dir == WEST
+ && strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
+ command[comlen++] = 'l';
+ else if (dir == EAST
+ && strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
+ command[comlen++] = 'h';
+ else if (dir == WEST)
+ command[comlen++] = 'h';
+ else
+ command[comlen++] = 'l';
+ break;
+ }
+}
+
+/*
+ * go for the closest mine if possible
+ */
+
+STATIC
+go_for_ammo(mine)
+ char mine;
+{
+ register int i, rel_dir, dist;
+
+ rel_dir = -1;
+ dist = WIDTH;
+ for (i = 0; i < NUMDIRECTIONS; i++) {
+ if (flbr[i].what == mine && flbr[i].distance < dist) {
+ rel_dir = i;
+ dist = flbr[i].distance;
+ }
+ }
+ if (rel_dir == -1)
+ return FALSE;
+
+ if (!(flbr[rel_dir].flags & ON_SIDE)
+ || flbr[rel_dir].distance > 1) {
+ if (dist > 4)
+ dist = 4;
+ face_and_move_direction(rel_dir, dist);
+ } else
+ return FALSE; /* until it's done right */
+ return TRUE;
+}
+
+STATIC
+wander()
+{
+ register int i, j, rel_dir, dir_mask, dir_count;
+
+ for (i = 0; i < NUMDIRECTIONS; i++)
+ if (!(flbr[i].flags & BEEN) || flbr[i].distance <= 1)
+ break;
+ if (i == NUMDIRECTIONS)
+ memset(been_there, 0, sizeof been_there);
+ dir_mask = dir_count = 0;
+ for (i = 0; i < NUMDIRECTIONS; i++) {
+ j = (RIGHT + i) % NUMDIRECTIONS;
+ if (flbr[j].distance <= 1 || flbr[j].flags & DEADEND)
+ continue;
+ if (!(flbr[j].flags & BEEN_SAME)) {
+ dir_mask = 1 << j;
+ dir_count = 1;
+ break;
+ }
+ if (j == FRONT
+ && num_turns > 4 + (random() %
+ ((flbr[FRONT].flags & BEEN) ? 7 : HEIGHT)))
+ continue;
+ dir_mask |= 1 << j;
+# ifdef notdef
+ dir_count++;
+# else
+ dir_count = 1;
+ break;
+# endif
+ }
+ if (dir_count == 0) {
+ duck(random() % NUMDIRECTIONS);
+ num_turns = 0;
+ return;
+ } else if (dir_count == 1)
+ rel_dir = ffs(dir_mask) - 1;
+ else {
+ rel_dir = ffs(dir_mask) - 1;
+ dir_mask &= ~(1 << rel_dir);
+ while (dir_mask != 0) {
+ i = ffs(dir_mask) - 1;
+ if (random() % 5 == 0)
+ rel_dir = i;
+ dir_mask &= ~(1 << i);
+ }
+ }
+ if (rel_dir == FRONT)
+ num_turns++;
+ else
+ num_turns = 0;
+
+# ifdef DEBUG
+ fprintf(debug, " w(%c)", RELCHARS[rel_dir]);
+# endif
+ face_and_move_direction(rel_dir, 1);
+}
+
+# endif /* OTTO */
diff --git a/hunt/hunt/playit.c b/hunt/hunt/playit.c
new file mode 100644
index 00000000..4a6046c9
--- /dev/null
+++ b/hunt/hunt/playit.c
@@ -0,0 +1,628 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# if defined(HPUX) || (defined(BSD_RELEASE) && BSD_RELEASE >= 44)
+# include <termios.h>
+# endif
+# include <curses.h>
+# include <ctype.h>
+# include <signal.h>
+# include <errno.h>
+# include "hunt.h"
+# include <sys/file.h>
+
+# ifndef FREAD
+# define FREAD 1
+# endif
+
+# if !defined(USE_CURSES) || !defined(TERMINFO)
+# define beep() (void) putchar(CTRL('G'))
+# endif
+# if !defined(USE_CURSES)
+# undef refresh
+# define refresh() (void) fflush(stdout);
+# endif
+# ifdef USE_CURSES
+# define clear_eol() clrtoeol()
+# define put_ch addch
+# define put_str addstr
+# endif
+
+int input();
+static int nchar_send;
+static int in = FREAD;
+# ifndef USE_CURSES
+char screen[SCREEN_HEIGHT][SCREEN_WIDTH2], blanks[SCREEN_WIDTH];
+int cur_row, cur_col;
+# endif
+# ifdef OTTO
+int Otto_count;
+int Otto_mode;
+static int otto_y, otto_x;
+static char otto_face;
+# endif
+
+# define MAX_SEND 5
+# define STDIN 0
+
+/*
+ * ibuf is the input buffer used for the stream from the driver.
+ * It is small because we do not check for user input when there
+ * are characters in the input buffer.
+ */
+static int icnt = 0;
+static unsigned char ibuf[256], *iptr = ibuf;
+static unsigned char getchr();
+
+#define GETCHR() (--icnt < 0 ? getchr() : *iptr++)
+
+#if !defined(BSD_RELEASE) || BSD_RELEASE < 44
+extern int _putchar();
+#endif
+
+/*
+ * playit:
+ * Play a given game, handling all the curses commands from
+ * the driver.
+ */
+playit()
+{
+ register int ch;
+ register int y, x;
+ extern int errno;
+ long version;
+
+ if (read(Socket, (char *) &version, LONGLEN) != LONGLEN) {
+ bad_con();
+ /* NOTREACHED */
+ }
+ if (ntohl(version) != HUNT_VERSION) {
+ bad_ver();
+ /* NOTREACHED */
+ }
+ errno = 0;
+# ifdef OTTO
+ Otto_count = 0;
+# endif
+ nchar_send = MAX_SEND;
+ while ((ch = GETCHR()) != EOF) {
+# ifdef DEBUG
+ fputc(ch, stderr);
+# endif
+ switch (ch & 0377) {
+ case MOVE:
+ y = GETCHR();
+ x = GETCHR();
+# ifdef USE_CURSES
+ move(y, x);
+# else
+ mvcur(cur_row, cur_col, y, x);
+ cur_row = y;
+ cur_col = x;
+# endif
+ break;
+ case ADDCH:
+ ch = GETCHR();
+# ifdef OTTO
+ switch (ch) {
+
+ case '<':
+ case '>':
+ case '^':
+ case 'v':
+ otto_face = ch;
+# ifdef USE_CURSES
+ getyx(stdscr, otto_y, otto_x);
+# else
+ otto_y = cur_row;
+ otto_x = cur_col;
+# endif
+ break;
+ }
+# endif
+ put_ch(ch);
+ break;
+ case CLRTOEOL:
+ clear_eol();
+ break;
+ case CLEAR:
+ clear_the_screen();
+ break;
+ case REFRESH:
+ refresh();
+ break;
+ case REDRAW:
+ redraw_screen();
+ refresh();
+ break;
+ case ENDWIN:
+ refresh();
+ if ((ch = GETCHR()) == LAST_PLAYER)
+ Last_player = TRUE;
+ ch = EOF;
+ goto out;
+ case BELL:
+ beep();
+ break;
+ case READY:
+ refresh();
+ if (nchar_send < 0)
+# if defined(HPUX) || (defined(BSD_RELEASE) && BSD_RELEASE >= 44)
+ tcflush(STDIN, TCIFLUSH);
+# else
+# ifndef TCFLSH
+ (void) ioctl(STDIN, TIOCFLUSH, &in);
+# else
+ (void) ioctl(STDIN, TCFLSH, 0);
+# endif
+# endif
+ nchar_send = MAX_SEND;
+# ifndef OTTO
+ (void) GETCHR();
+# else
+ Otto_count -= (GETCHR() & 0xff);
+ if (!Am_monitor) {
+# ifdef DEBUG
+ fputc('0' + Otto_count, stderr);
+# endif
+ if (Otto_count == 0 && Otto_mode)
+ otto(otto_y, otto_x, otto_face);
+ }
+# endif
+ break;
+ default:
+# ifdef OTTO
+ switch (ch) {
+
+ case '<':
+ case '>':
+ case '^':
+ case 'v':
+ otto_face = ch;
+# ifdef USE_CURSES
+ getyx(stdscr, otto_y, otto_x);
+# else
+ otto_y = cur_row;
+ otto_x = cur_col;
+# endif
+ break;
+ }
+# endif
+ put_ch(ch);
+ break;
+ }
+ }
+out:
+ (void) close(Socket);
+}
+
+/*
+ * getchr:
+ * Grab input and pass it along to the driver
+ * Return any characters from the driver
+ * When this routine is called by GETCHR, we already know there are
+ * no characters in the input buffer.
+ */
+static
+unsigned char
+getchr()
+{
+ long readfds, s_readfds;
+ int driver_mask, stdin_mask;
+ int nfds, s_nfds;
+
+ driver_mask = 1L << Socket;
+ stdin_mask = 1L << STDIN;
+ s_readfds = driver_mask | stdin_mask;
+ s_nfds = (Socket > STDIN) ? Socket : STDIN;
+ s_nfds++;
+
+one_more_time:
+ do {
+ errno = 0;
+ readfds = s_readfds;
+ nfds = s_nfds;
+ nfds = select(nfds, &readfds, NULL, NULL, NULL);
+ } while (nfds <= 0 && errno == EINTR);
+
+ if (readfds & stdin_mask)
+ send_stuff();
+ if ((readfds & driver_mask) == 0)
+ goto one_more_time;
+ icnt = read(Socket, ibuf, sizeof ibuf);
+ if (icnt < 0) {
+ bad_con();
+ /* NOTREACHED */
+ }
+ if (icnt == 0)
+ goto one_more_time;
+ iptr = ibuf;
+ icnt--;
+ return *iptr++;
+}
+
+/*
+ * send_stuff:
+ * Send standard input characters to the driver
+ */
+send_stuff()
+{
+ register int count;
+ register char *sp, *nsp;
+ static char inp[sizeof Buf];
+
+ count = read(STDIN, Buf, sizeof Buf);
+ if (count <= 0)
+ return;
+ if (nchar_send <= 0 && !no_beep) {
+ (void) write(1, "\7", 1); /* CTRL('G') */
+ return;
+ }
+
+ /*
+ * look for 'q'uit commands; if we find one,
+ * confirm it. If it is not confirmed, strip
+ * it out of the input
+ */
+ Buf[count] = '\0';
+ nsp = inp;
+ for (sp = Buf; *sp != '\0'; sp++)
+ if ((*nsp = map_key[*sp]) == 'q')
+ intr();
+ else
+ nsp++;
+ count = nsp - inp;
+ if (count) {
+# ifdef OTTO
+ Otto_count += count;
+# endif
+ nchar_send -= count;
+ if (nchar_send < 0)
+ count += nchar_send;
+ (void) write(Socket, inp, count);
+ }
+}
+
+/*
+ * quit:
+ * Handle the end of the game when the player dies
+ */
+long
+quit(old_status)
+long old_status;
+{
+ register int explain, ch;
+
+ if (Last_player)
+ return Q_QUIT;
+# ifdef OTTO
+ if (Otto_mode)
+ return Q_CLOAK;
+# endif
+# ifdef USE_CURSES
+ move(HEIGHT, 0);
+# else
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ cur_row = HEIGHT;
+ cur_col = 0;
+# endif
+ put_str("Re-enter game [ynwo]? ");
+ clear_eol();
+ explain = FALSE;
+ for (;;) {
+ refresh();
+ if (isupper(ch = getchar()))
+ ch = tolower(ch);
+ if (ch == 'y')
+ return old_status;
+ else if (ch == 'o')
+ break;
+ else if (ch == 'n') {
+# ifndef INTERNET
+ return Q_QUIT;
+# else
+# ifdef USE_CURSES
+ move(HEIGHT, 0);
+# else
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ cur_row = HEIGHT;
+ cur_col = 0;
+# endif
+ put_str("Write a parting message [yn]? ");
+ clear_eol();
+ refresh();
+ for (;;) {
+ if (isupper(ch = getchar()))
+ ch = tolower(ch);
+ if (ch == 'y')
+ goto get_message;
+ if (ch == 'n')
+ return Q_QUIT;
+ }
+# endif
+ }
+# ifdef INTERNET
+ else if (ch == 'w') {
+ static char buf[WIDTH + WIDTH % 2];
+ char *cp, c;
+
+get_message:
+ c = ch; /* save how we got here */
+# ifdef USE_CURSES
+ move(HEIGHT, 0);
+# else
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ cur_row = HEIGHT;
+ cur_col = 0;
+# endif
+ put_str("Message: ");
+ clear_eol();
+ refresh();
+ cp = buf;
+ for (;;) {
+ refresh();
+ if ((ch = getchar()) == '\n' || ch == '\r')
+ break;
+# if defined(TERMINFO) || BSD_RELEASE >= 44
+ if (ch == erasechar())
+# else
+ if (ch == _tty.sg_erase)
+# endif
+ {
+ if (cp > buf) {
+# ifdef USE_CURSES
+ int y, x;
+ getyx(stdscr, y, x);
+ move(y, x - 1);
+# else
+ mvcur(cur_row, cur_col, cur_row,
+ cur_col - 1);
+ cur_col -= 1;
+# endif
+ cp -= 1;
+ clear_eol();
+ }
+ continue;
+ }
+# if defined(TERMINFO) || BSD_RELEASE >= 44
+ else if (ch == killchar())
+# else
+ else if (ch == _tty.sg_kill)
+# endif
+ {
+# ifdef USE_CURSES
+ int y, x;
+ getyx(stdscr, y, x);
+ move(y, x - (cp - buf));
+# else
+ mvcur(cur_row, cur_col, cur_row,
+ cur_col - (cp - buf));
+ cur_col -= cp - buf;
+# endif
+ cp = buf;
+ clear_eol();
+ continue;
+ } else if (!isprint(ch)) {
+ beep();
+ continue;
+ }
+ put_ch(ch);
+ *cp++ = ch;
+ if (cp + 1 >= buf + sizeof buf)
+ break;
+ }
+ *cp = '\0';
+ Send_message = buf;
+ return (c == 'w') ? old_status : Q_MESSAGE;
+ }
+# endif
+ beep();
+ if (!explain) {
+ put_str("(Yes, No, Write message, or Options) ");
+ explain = TRUE;
+ }
+ }
+
+# ifdef USE_CURSES
+ move(HEIGHT, 0);
+# else
+ mvcur(cur_row, cur_col, HEIGHT, 0);
+ cur_row = HEIGHT;
+ cur_col = 0;
+# endif
+# ifdef FLY
+ put_str("Scan, Cloak, Flying, or Quit? ");
+# else
+ put_str("Scan, Cloak, or Quit? ");
+# endif
+ clear_eol();
+ refresh();
+ explain = FALSE;
+ for (;;) {
+ if (isupper(ch = getchar()))
+ ch = tolower(ch);
+ if (ch == 's')
+ return Q_SCAN;
+ else if (ch == 'c')
+ return Q_CLOAK;
+# ifdef FLY
+ else if (ch == 'f')
+ return Q_FLY;
+# endif
+ else if (ch == 'q')
+ return Q_QUIT;
+ beep();
+ if (!explain) {
+# ifdef FLY
+ put_str("[SCFQ] ");
+# else
+ put_str("[SCQ] ");
+# endif
+ explain = TRUE;
+ }
+ refresh();
+ }
+}
+
+# ifndef USE_CURSES
+put_ch(ch)
+ char ch;
+{
+ if (!isprint(ch)) {
+ fprintf(stderr, "r,c,ch: %d,%d,%d", cur_row, cur_col, ch);
+ return;
+ }
+ screen[cur_row][cur_col] = ch;
+ putchar(ch);
+ if (++cur_col >= COLS) {
+ if (!AM || XN)
+ putchar('\n');
+ cur_col = 0;
+ if (++cur_row >= LINES)
+ cur_row = LINES;
+ }
+}
+
+put_str(s)
+ char *s;
+{
+ while (*s)
+ put_ch(*s++);
+}
+# endif
+
+clear_the_screen()
+{
+# ifdef USE_CURSES
+ clear();
+ move(0, 0);
+ refresh();
+# else
+ register int i;
+
+ if (blanks[0] == '\0')
+ for (i = 0; i < SCREEN_WIDTH; i++)
+ blanks[i] = ' ';
+
+ if (CL != NULL) {
+#if !defined(BSD_RELEASE) || BSD_RELEASE < 44
+ tputs(CL, LINES, _putchar);
+#else
+ tputs(CL, LINES, __cputchar);
+#endif
+ for (i = 0; i < SCREEN_HEIGHT; i++)
+ memcpy(screen[i], blanks, SCREEN_WIDTH);
+ } else {
+ for (i = 0; i < SCREEN_HEIGHT; i++) {
+ mvcur(cur_row, cur_col, i, 0);
+ cur_row = i;
+ cur_col = 0;
+ clear_eol();
+ }
+ mvcur(cur_row, cur_col, 0, 0);
+ }
+ cur_row = cur_col = 0;
+#endif
+}
+
+#ifndef USE_CURSES
+clear_eol()
+{
+ if (CE != NULL)
+#if !defined(BSD_RELEASE) || BSD_RELEASE < 44
+ tputs(CE, 1, _putchar);
+#else
+ tputs(CE, 1, __cputchar);
+#endif
+ else {
+ fwrite(blanks, sizeof (char), SCREEN_WIDTH - cur_col, stdout);
+ if (COLS != SCREEN_WIDTH)
+ mvcur(cur_row, SCREEN_WIDTH, cur_row, cur_col);
+ else if (AM)
+ mvcur(cur_row + 1, 0, cur_row, cur_col);
+ else
+ mvcur(cur_row, SCREEN_WIDTH - 1, cur_row, cur_col);
+ }
+ memcpy(&screen[cur_row][cur_col], blanks, SCREEN_WIDTH - cur_col);
+}
+# endif
+
+redraw_screen()
+{
+# ifdef USE_CURSES
+ clearok(stdscr, TRUE);
+ touchwin(stdscr);
+# else
+ register int i;
+# ifndef NOCURSES
+ static int first = 1;
+
+ if (first) {
+ curscr = newwin(SCREEN_HEIGHT, SCREEN_WIDTH, 0, 0);
+ if (curscr == NULL) {
+ fprintf(stderr, "Can't create curscr\n");
+ exit(1);
+ }
+# if !defined(BSD_RELEASE) || BSD_RELEASE < 44
+ for (i = 0; i < SCREEN_HEIGHT; i++)
+ curscr->_y[i] = screen[i];
+# endif
+ first = 0;
+ }
+# if defined(BSD_RELEASE) && BSD_RELEASE >= 44
+ for (i = 0; i < SCREEN_HEIGHT; i++) {
+ register int j;
+ for (j = 0; j < SCREEN_WIDTH; j++)
+ curscr->lines[i]->line[j].ch = screen[i][j];
+ }
+ curscr->cury = cur_row;
+ curscr->curx = cur_col;
+# else
+ curscr->_cury = cur_row;
+ curscr->_curx = cur_col;
+# endif
+ clearok(curscr, TRUE);
+ touchwin(curscr);
+ wrefresh(curscr);
+#else
+ mvcur(cur_row, cur_col, 0, 0);
+ for (i = 0; i < SCREEN_HEIGHT - 1; i++) {
+ fwrite(screen[i], sizeof (char), SCREEN_WIDTH, stdout);
+ if (COLS > SCREEN_WIDTH || (COLS == SCREEN_WIDTH && !AM))
+ putchar('\n');
+ }
+ fwrite(screen[SCREEN_HEIGHT - 1], sizeof (char), SCREEN_WIDTH - 1,
+ stdout);
+ mvcur(SCREEN_HEIGHT - 1, SCREEN_WIDTH - 1, cur_row, cur_col);
+#endif
+#endif
+}
+
+/*
+ * do_message:
+ * Send a message to the driver and return
+ */
+do_message()
+{
+ extern int errno;
+ long version;
+
+ if (read(Socket, (char *) &version, LONGLEN) != LONGLEN) {
+ bad_con();
+ /* NOTREACHED */
+ }
+ if (ntohl(version) != HUNT_VERSION) {
+ bad_ver();
+ /* NOTREACHED */
+ }
+# ifdef INTERNET
+ if (write(Socket, Send_message, strlen(Send_message)) < 0) {
+ bad_con();
+ /* NOTREACHED */
+ }
+# endif
+ (void) close(Socket);
+}
diff --git a/hunt/huntd/answer.c b/hunt/huntd/answer.c
new file mode 100644
index 00000000..0de92027
--- /dev/null
+++ b/hunt/huntd/answer.c
@@ -0,0 +1,389 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+# include <ctype.h>
+# include <errno.h>
+# include <fcntl.h>
+
+# define SCOREDECAY 15
+
+static char Ttyname[NAMELEN];
+
+answer()
+{
+ register PLAYER *pp;
+ register int newsock;
+ static u_long mode;
+ static char name[NAMELEN];
+ static char team;
+ static int enter_status;
+ static int socklen;
+ static u_long machine;
+ static u_long uid;
+ static SOCKET sockstruct;
+ register char *cp1, *cp2;
+ int flags;
+ long version;
+
+# ifdef INTERNET
+ socklen = sizeof sockstruct;
+# else
+ socklen = sizeof sockstruct - 1;
+# endif
+ errno = 0;
+ newsock = accept(Socket, (struct sockaddr *) &sockstruct, &socklen);
+ if (newsock < 0)
+ {
+ if (errno == EINTR)
+ return FALSE;
+# ifdef LOG
+ syslog(LOG_ERR, "accept: %m");
+# else
+ perror("accept");
+# endif
+ cleanup(1);
+ }
+
+# ifdef INTERNET
+ machine = ntohl(((struct sockaddr_in *) &sockstruct)->sin_addr.s_addr);
+# else
+ if (machine == 0)
+ machine = gethostid();
+# endif
+ version = htonl((unsigned long) HUNT_VERSION);
+ (void) write(newsock, (char *) &version, LONGLEN);
+ (void) read(newsock, (char *) &uid, LONGLEN);
+ uid = ntohl((unsigned long) uid);
+ (void) read(newsock, name, NAMELEN);
+ (void) read(newsock, &team, 1);
+ (void) read(newsock, (char *) &enter_status, LONGLEN);
+ enter_status = ntohl((unsigned long) enter_status);
+ (void) read(newsock, Ttyname, NAMELEN);
+ (void) read(newsock, (char *) &mode, sizeof mode);
+ mode = ntohl(mode);
+
+ /*
+ * Turn off blocking I/O, so a slow or dead terminal won't stop
+ * the game. All subsequent reads check how many bytes they read.
+ */
+ flags = fcntl(newsock, F_GETFL, 0);
+ flags |= O_NDELAY;
+ (void) fcntl(newsock, F_SETFL, flags);
+
+ /*
+ * Make sure the name contains only printable characters
+ * since we use control characters for cursor control
+ * between driver and player processes
+ */
+ for (cp1 = cp2 = name; *cp1 != '\0'; cp1++)
+ if (isprint(*cp1) || *cp1 == ' ')
+ *cp2++ = *cp1;
+ *cp2 = '\0';
+
+# ifdef INTERNET
+ if (mode == C_MESSAGE) {
+ char buf[BUFSIZ + 1];
+ int n;
+
+ if (team == ' ')
+ (void) sprintf(buf, "%s: ", name);
+ else
+ (void) sprintf(buf, "%s[%c]: ", name, team);
+ n = strlen(buf);
+ for (pp = Player; pp < End_player; pp++) {
+ cgoto(pp, HEIGHT, 0);
+ outstr(pp, buf, n);
+ }
+ while ((n = read(newsock, buf, BUFSIZ)) > 0)
+ for (pp = Player; pp < End_player; pp++)
+ outstr(pp, buf, n);
+ for (pp = Player; pp < End_player; pp++) {
+ ce(pp);
+ sendcom(pp, REFRESH);
+ sendcom(pp, READY, 0);
+ (void) fflush(pp->p_output);
+ }
+ (void) close(newsock);
+ return FALSE;
+ }
+ else
+# endif
+# ifdef MONITOR
+ if (mode == C_MONITOR)
+ if (End_monitor < &Monitor[MAXMON])
+ pp = End_monitor++;
+ else {
+ socklen = 0;
+ (void) write(newsock, (char *) &socklen,
+ sizeof socklen);
+ (void) close(newsock);
+ return FALSE;
+ }
+ else
+# endif
+ if (End_player < &Player[MAXPL])
+ pp = End_player++;
+ else {
+ socklen = 0;
+ (void) write(newsock, (char *) &socklen,
+ sizeof socklen);
+ (void) close(newsock);
+ return FALSE;
+ }
+
+#ifdef MONITOR
+ if (mode == C_MONITOR && team == ' ')
+ team = '*';
+#endif
+ pp->p_ident = get_ident(machine, uid, name, team);
+ pp->p_output = fdopen(newsock, "w");
+ pp->p_death[0] = '\0';
+ pp->p_fd = newsock;
+ pp->p_mask = (1 << pp->p_fd);
+ Fds_mask |= pp->p_mask;
+ if (pp->p_fd >= Num_fds)
+ Num_fds = pp->p_fd + 1;
+
+ pp->p_y = 0;
+ pp->p_x = 0;
+
+# ifdef MONITOR
+ if (mode == C_MONITOR)
+ stmonitor(pp);
+ else
+# endif
+ stplayer(pp, enter_status);
+ return TRUE;
+}
+
+# ifdef MONITOR
+stmonitor(pp)
+register PLAYER *pp;
+{
+ register int line;
+ register PLAYER *npp;
+
+ memcpy(pp->p_maze, Maze, sizeof Maze);
+
+ drawmaze(pp);
+
+ (void) sprintf(Buf, "%5.5s%c%-10.10s %c", " ", stat_char(pp),
+ pp->p_ident->i_name, pp->p_ident->i_team);
+ line = STAT_MON_ROW + 1 + (pp - Monitor);
+ for (npp = Player; npp < End_player; npp++) {
+ cgoto(npp, line, STAT_NAME_COL);
+ outstr(npp, Buf, STAT_NAME_LEN);
+ }
+ for (npp = Monitor; npp < End_monitor; npp++) {
+ cgoto(npp, line, STAT_NAME_COL);
+ outstr(npp, Buf, STAT_NAME_LEN);
+ }
+
+ sendcom(pp, REFRESH);
+ sendcom(pp, READY, 0);
+ (void) fflush(pp->p_output);
+}
+# endif
+
+stplayer(newpp, enter_status)
+register PLAYER *newpp;
+int enter_status;
+{
+ register int x, y;
+ register PLAYER *pp;
+
+ Nplayer++;
+
+ for (y = 0; y < UBOUND; y++)
+ for (x = 0; x < WIDTH; x++)
+ newpp->p_maze[y][x] = Maze[y][x];
+ for ( ; y < DBOUND; y++) {
+ for (x = 0; x < LBOUND; x++)
+ newpp->p_maze[y][x] = Maze[y][x];
+ for ( ; x < RBOUND; x++)
+ newpp->p_maze[y][x] = SPACE;
+ for ( ; x < WIDTH; x++)
+ newpp->p_maze[y][x] = Maze[y][x];
+ }
+ for ( ; y < HEIGHT; y++)
+ for (x = 0; x < WIDTH; x++)
+ newpp->p_maze[y][x] = Maze[y][x];
+
+ do {
+ x = rand_num(WIDTH - 1) + 1;
+ y = rand_num(HEIGHT - 1) + 1;
+ } while (Maze[y][x] != SPACE);
+ newpp->p_over = SPACE;
+ newpp->p_x = x;
+ newpp->p_y = y;
+ newpp->p_undershot = FALSE;
+
+# ifdef FLY
+ if (enter_status == Q_FLY) {
+ newpp->p_flying = rand_num(20);
+ newpp->p_flyx = 2 * rand_num(6) - 5;
+ newpp->p_flyy = 2 * rand_num(6) - 5;
+ newpp->p_face = FLYER;
+ }
+ else
+# endif
+ {
+ newpp->p_flying = -1;
+ newpp->p_face = rand_dir();
+ }
+ newpp->p_damage = 0;
+ newpp->p_damcap = MAXDAM;
+ newpp->p_nchar = 0;
+ newpp->p_ncount = 0;
+ newpp->p_nexec = 0;
+ newpp->p_ammo = ISHOTS;
+# ifdef BOOTS
+ newpp->p_nboots = 0;
+# endif
+ if (enter_status == Q_SCAN) {
+ newpp->p_scan = SCANLEN;
+ newpp->p_cloak = 0;
+ }
+ else {
+ newpp->p_scan = 0;
+ newpp->p_cloak = CLOAKLEN;
+ }
+ newpp->p_ncshot = 0;
+
+ do {
+ x = rand_num(WIDTH - 1) + 1;
+ y = rand_num(HEIGHT - 1) + 1;
+ } while (Maze[y][x] != SPACE);
+ Maze[y][x] = GMINE;
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, y, x);
+# endif
+
+ do {
+ x = rand_num(WIDTH - 1) + 1;
+ y = rand_num(HEIGHT - 1) + 1;
+ } while (Maze[y][x] != SPACE);
+ Maze[y][x] = MINE;
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, y, x);
+# endif
+
+ (void) sprintf(Buf, "%5.2f%c%-10.10s %c", newpp->p_ident->i_score,
+ stat_char(newpp), newpp->p_ident->i_name,
+ newpp->p_ident->i_team);
+ y = STAT_PLAY_ROW + 1 + (newpp - Player);
+ for (pp = Player; pp < End_player; pp++) {
+ if (pp != newpp) {
+ char smallbuf[10];
+
+ pp->p_ammo += NSHOTS;
+ newpp->p_ammo += NSHOTS;
+ cgoto(pp, y, STAT_NAME_COL);
+ outstr(pp, Buf, STAT_NAME_LEN);
+ (void) sprintf(smallbuf, "%3d", pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, smallbuf, 3);
+ }
+ }
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++) {
+ cgoto(pp, y, STAT_NAME_COL);
+ outstr(pp, Buf, STAT_NAME_LEN);
+ }
+# endif
+
+ drawmaze(newpp);
+ drawplayer(newpp, TRUE);
+ look(newpp);
+# ifdef FLY
+ if (enter_status == Q_FLY)
+ /* Make sure that the position you enter in will be erased */
+ showexpl(newpp->p_y, newpp->p_x, FLYER);
+# endif
+ sendcom(newpp, REFRESH);
+ sendcom(newpp, READY, 0);
+ (void) fflush(newpp->p_output);
+}
+
+/*
+ * rand_dir:
+ * Return a random direction
+ */
+rand_dir()
+{
+ switch (rand_num(4)) {
+ case 0:
+ return LEFTS;
+ case 1:
+ return RIGHT;
+ case 2:
+ return BELOW;
+ case 3:
+ return ABOVE;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * get_ident:
+ * Get the score structure of a player
+ */
+IDENT *
+get_ident(machine, uid, name, team)
+u_long machine;
+u_long uid;
+char *name;
+char team;
+{
+ register IDENT *ip;
+ static IDENT punt;
+
+ for (ip = Scores; ip != NULL; ip = ip->i_next)
+ if (ip->i_machine == machine
+ && ip->i_uid == uid
+ && ip->i_team == team
+ && strncmp(ip->i_name, name, NAMELEN) == 0)
+ break;
+
+ if (ip != NULL) {
+ if (ip->i_entries < SCOREDECAY)
+ ip->i_entries++;
+ else
+ ip->i_kills = (ip->i_kills * (SCOREDECAY - 1))
+ / SCOREDECAY;
+ ip->i_score = ip->i_kills / (double) ip->i_entries;
+ }
+ else {
+ ip = (IDENT *) malloc(sizeof (IDENT));
+ if (ip == NULL) {
+ /* Fourth down, time to punt */
+ ip = &punt;
+ }
+ ip->i_machine = machine;
+ ip->i_team = team;
+ ip->i_uid = uid;
+ strncpy(ip->i_name, name, NAMELEN);
+ ip->i_kills = 0;
+ ip->i_entries = 1;
+ ip->i_score = 0;
+ ip->i_absorbed = 0;
+ ip->i_faced = 0;
+ ip->i_shot = 0;
+ ip->i_robbed = 0;
+ ip->i_slime = 0;
+ ip->i_missed = 0;
+ ip->i_ducked = 0;
+ ip->i_gkills = ip->i_bkills = ip->i_deaths = 0;
+ ip->i_stillb = ip->i_saved = 0;
+ ip->i_next = Scores;
+ Scores = ip;
+ }
+
+ return ip;
+}
diff --git a/hunt/huntd/bsd.h b/hunt/huntd/bsd.h
new file mode 100644
index 00000000..c186be88
--- /dev/null
+++ b/hunt/huntd/bsd.h
@@ -0,0 +1,15 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# if defined(BSD_RELEASE) && BSD_RELEASE >= 43
+# define BROADCAST
+# define SYSLOG_43
+# define TALK_43
+# endif
+# if defined(BSD_RELEASE) && BSD_RELEASE == 42
+# define SYSLOG_42
+# define TALK_42
+# endif
diff --git a/hunt/huntd/ctl.c b/hunt/huntd/ctl.c
new file mode 100644
index 00000000..59449930
--- /dev/null
+++ b/hunt/huntd/ctl.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#include "bsd.h"
+
+#if defined(TALK_43) || defined(TALK_42)
+
+#ifndef lint
+static char sccsid[] = "@(#)ctl.c 5.2 (Berkeley) 3/13/86";
+#endif
+
+/*
+ * This file handles haggling with the various talk daemons to
+ * get a socket to talk to. sockt is opened and connected in
+ * the progress
+ */
+
+#include "talk_ctl.h"
+
+struct sockaddr_in daemon_addr = { AF_INET };
+struct sockaddr_in ctl_addr = { AF_INET };
+
+ /* inet addresses of the two machines */
+struct in_addr my_machine_addr;
+struct in_addr his_machine_addr;
+
+u_short daemon_port; /* port number of the talk daemon */
+
+int ctl_sockt;
+
+CTL_MSG msg;
+
+/* open the ctl socket */
+open_ctl()
+{
+ int length;
+
+ ctl_addr.sin_port = 0;
+ ctl_addr.sin_addr = my_machine_addr;
+ ctl_sockt = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ctl_sockt <= 0)
+ p_error("Bad socket");
+ if (bind(ctl_sockt, (struct sockaddr *)&ctl_addr, sizeof(ctl_addr)) != 0)
+ p_error("Couldn't bind to control socket");
+ length = sizeof(ctl_addr);
+ if (getsockname(ctl_sockt, (struct sockaddr *) &ctl_addr, &length) < 0)
+ p_error("Bad address for ctl socket");
+}
+#endif
diff --git a/hunt/huntd/ctl_transact.c b/hunt/huntd/ctl_transact.c
new file mode 100644
index 00000000..1f23c487
--- /dev/null
+++ b/hunt/huntd/ctl_transact.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#include "bsd.h"
+
+#if defined(TALK_43) || defined(TALK_42)
+
+#ifndef lint
+static char sccsid[] = "@(#)ctl_transact.c 5.2 (Berkeley) 3/13/86";
+#endif
+
+#include "talk_ctl.h"
+#include <sys/time.h>
+
+#define CTL_WAIT 2 /* time to wait for a response, in seconds */
+#define MAX_RETRY 5
+
+/*
+ * SOCKDGRAM is unreliable, so we must repeat messages if we have
+ * not recieved an acknowledgement within a reasonable amount
+ * of time
+ */
+ctl_transact(target, msg, type, rp)
+ struct in_addr target;
+ CTL_MSG msg;
+ int type;
+ CTL_RESPONSE *rp;
+{
+ int read_mask, ctl_mask, nready, cc, retries;
+ struct timeval wait;
+
+ msg.type = type;
+ daemon_addr.sin_addr = target;
+ daemon_addr.sin_port = daemon_port;
+ ctl_mask = 1 << ctl_sockt;
+
+ /*
+ * Keep sending the message until a response of
+ * the proper type is obtained.
+ */
+ do {
+ wait.tv_sec = CTL_WAIT;
+ wait.tv_usec = 0;
+ /* resend message until a response is obtained */
+ for (retries = MAX_RETRY; retries > 0; retries -= 1) {
+ cc = sendto(ctl_sockt, (char *)&msg, sizeof (msg), 0,
+ (struct sockaddr *)&daemon_addr, sizeof (daemon_addr));
+ if (cc != sizeof (msg)) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error on write to talk daemon");
+ }
+ read_mask = ctl_mask;
+ nready = select(32, &read_mask, 0, 0, &wait);
+ if (nready < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error waiting for daemon response");
+ }
+ if (nready != 0)
+ break;
+ }
+ if (retries <= 0)
+ break;
+ /*
+ * Keep reading while there are queued messages
+ * (this is not necessary, it just saves extra
+ * request/acknowledgements being sent)
+ */
+ do {
+ cc = recv(ctl_sockt, (char *)rp, sizeof (*rp), 0);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error on read from talk daemon");
+ }
+ read_mask = ctl_mask;
+ /* an immediate poll */
+ timerclear(&wait);
+ nready = select(32, &read_mask, 0, 0, &wait);
+ } while (nready > 0 && (
+#ifdef TALK_43
+ rp->vers != TALK_VERSION ||
+#endif
+ rp->type != type));
+ } while (
+#ifdef TALK_43
+ rp->vers != TALK_VERSION ||
+#endif
+ rp->type != type);
+ rp->id_num = ntohl(rp->id_num);
+#ifdef TALK_43
+ rp->addr.sa_family = ntohs(rp->addr.sa_family);
+# else
+ rp->addr.sin_family = ntohs(rp->addr.sin_family);
+# endif
+}
+#endif
diff --git a/hunt/huntd/draw.c b/hunt/huntd/draw.c
new file mode 100644
index 00000000..bbadbe16
--- /dev/null
+++ b/hunt/huntd/draw.c
@@ -0,0 +1,365 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+
+drawmaze(pp)
+register PLAYER *pp;
+{
+ register int x;
+ register char *sp;
+ register int y;
+ register char *endp;
+
+ clrscr(pp);
+ outstr(pp, pp->p_maze[0], WIDTH);
+ for (y = 1; y < HEIGHT - 1; y++) {
+ endp = &pp->p_maze[y][WIDTH];
+ for (x = 0, sp = pp->p_maze[y]; sp < endp; x++, sp++)
+ if (*sp != SPACE) {
+ cgoto(pp, y, x);
+ if (pp->p_x == x && pp->p_y == y)
+ outch(pp, translate(*sp));
+ else if (isplayer(*sp))
+ outch(pp, player_sym(pp, y, x));
+ else
+ outch(pp, *sp);
+ }
+ }
+ cgoto(pp, HEIGHT - 1, 0);
+ outstr(pp, pp->p_maze[HEIGHT - 1], WIDTH);
+ drawstatus(pp);
+}
+
+/*
+ * drawstatus - put up the status lines (this assumes the screen
+ * size is 80x24 with the maze being 64x24)
+ */
+drawstatus(pp)
+register PLAYER *pp;
+{
+ register int i;
+ register PLAYER *np;
+
+ cgoto(pp, STAT_AMMO_ROW, STAT_LABEL_COL);
+ outstr(pp, "Ammo:", 5);
+ (void) sprintf(Buf, "%3d", pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+
+ cgoto(pp, STAT_GUN_ROW, STAT_LABEL_COL);
+ outstr(pp, "Gun:", 4);
+ cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
+ outstr(pp, (pp->p_ncshot < MAXNCSHOT) ? " ok" : " ", 3);
+
+ cgoto(pp, STAT_DAM_ROW, STAT_LABEL_COL);
+ outstr(pp, "Damage:", 7);
+ (void) sprintf(Buf, "%2d/%2d", pp->p_damage, pp->p_damcap);
+ cgoto(pp, STAT_DAM_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 5);
+
+ cgoto(pp, STAT_KILL_ROW, STAT_LABEL_COL);
+ outstr(pp, "Kills:", 6);
+ (void) sprintf(Buf, "%3d", (pp->p_damcap - MAXDAM) / 2);
+ cgoto(pp, STAT_KILL_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+
+ cgoto(pp, STAT_PLAY_ROW, STAT_LABEL_COL);
+ outstr(pp, "Player:", 7);
+ for (i = STAT_PLAY_ROW + 1, np = Player; np < End_player; np++) {
+ (void) sprintf(Buf, "%5.2f%c%-10.10s %c", np->p_ident->i_score,
+ stat_char(np), np->p_ident->i_name,
+ np->p_ident->i_team);
+ cgoto(pp, i++, STAT_NAME_COL);
+ outstr(pp, Buf, STAT_NAME_LEN);
+ }
+
+# ifdef MONITOR
+ cgoto(pp, STAT_MON_ROW, STAT_LABEL_COL);
+ outstr(pp, "Monitor:", 8);
+ for (i = STAT_MON_ROW + 1, np = Monitor; np < End_monitor; np++) {
+ (void) sprintf(Buf, "%5.5s %-10.10s %c", " ",
+ np->p_ident->i_name, np->p_ident->i_team);
+ cgoto(pp, i++, STAT_NAME_COL);
+ outstr(pp, Buf, STAT_NAME_LEN);
+ }
+# endif
+}
+
+look(pp)
+register PLAYER *pp;
+{
+ register int x, y;
+
+ x = pp->p_x;
+ y = pp->p_y;
+
+ check(pp, y - 1, x - 1);
+ check(pp, y - 1, x );
+ check(pp, y - 1, x + 1);
+ check(pp, y , x - 1);
+ check(pp, y , x );
+ check(pp, y , x + 1);
+ check(pp, y + 1, x - 1);
+ check(pp, y + 1, x );
+ check(pp, y + 1, x + 1);
+
+ switch (pp->p_face) {
+ case LEFTS:
+ see(pp, LEFTS);
+ see(pp, ABOVE);
+ see(pp, BELOW);
+ break;
+ case RIGHT:
+ see(pp, RIGHT);
+ see(pp, ABOVE);
+ see(pp, BELOW);
+ break;
+ case ABOVE:
+ see(pp, ABOVE);
+ see(pp, LEFTS);
+ see(pp, RIGHT);
+ break;
+ case BELOW:
+ see(pp, BELOW);
+ see(pp, LEFTS);
+ see(pp, RIGHT);
+ break;
+# ifdef FLY
+ case FLYER:
+ break;
+# endif
+ }
+ cgoto(pp, y, x);
+}
+
+see(pp, face)
+register PLAYER *pp;
+int face;
+{
+ register char *sp;
+ register int y, x, i, cnt;
+
+ x = pp->p_x;
+ y = pp->p_y;
+
+ switch (face) {
+ case LEFTS:
+ sp = &Maze[y][x];
+ for (i = 0; See_over[*--sp]; i++)
+ continue;
+
+ if (i == 0)
+ break;
+
+ cnt = i;
+ x = pp->p_x - 1;
+ --y;
+ while (i--)
+ check(pp, y, --x);
+ i = cnt;
+ x = pp->p_x - 1;
+ ++y;
+ while (i--)
+ check(pp, y, --x);
+ i = cnt;
+ x = pp->p_x - 1;
+ ++y;
+ while (i--)
+ check(pp, y, --x);
+ break;
+ case RIGHT:
+ sp = &Maze[y][++x];
+ for (i = 0; See_over[*sp++]; i++)
+ continue;
+
+ if (i == 0)
+ break;
+
+ cnt = i;
+ x = pp->p_x + 1;
+ --y;
+ while (i--)
+ check(pp, y, ++x);
+ i = cnt;
+ x = pp->p_x + 1;
+ ++y;
+ while (i--)
+ check(pp, y, ++x);
+ i = cnt;
+ x = pp->p_x + 1;
+ ++y;
+ while (i--)
+ check(pp, y, ++x);
+ break;
+ case ABOVE:
+ sp = &Maze[--y][x];
+ if (!See_over[*sp])
+ break;
+ do {
+ --y;
+ sp -= sizeof Maze[0];
+ check(pp, y, x - 1);
+ check(pp, y, x );
+ check(pp, y, x + 1);
+ } while (See_over[*sp]);
+ break;
+ case BELOW:
+ sp = &Maze[++y][x];
+ if (!See_over[*sp])
+ break;
+ do {
+ y++;
+ sp += sizeof Maze[0];
+ check(pp, y, x - 1);
+ check(pp, y, x );
+ check(pp, y, x + 1);
+ } while (See_over[*sp]);
+ break;
+ }
+}
+
+check(pp, y, x)
+PLAYER *pp;
+int y, x;
+{
+ register int index;
+ register int ch;
+ register PLAYER *rpp;
+
+ index = y * sizeof Maze[0] + x;
+ ch = ((char *) Maze)[index];
+ if (ch != ((char *) pp->p_maze)[index]) {
+ rpp = pp;
+ cgoto(rpp, y, x);
+ if (x == rpp->p_x && y == rpp->p_y)
+ outch(rpp, translate(ch));
+ else if (isplayer(ch))
+ outch(rpp, player_sym(rpp, y, x));
+ else
+ outch(rpp, ch);
+ ((char *) rpp->p_maze)[index] = ch;
+ }
+}
+
+/*
+ * showstat
+ * Update the status of players
+ */
+showstat(pp)
+register PLAYER *pp;
+{
+ register PLAYER *np;
+ register int y;
+ register char c;
+
+ y = STAT_PLAY_ROW + 1 + (pp - Player);
+ c = stat_char(pp);
+# ifdef MONITOR
+ for (np = Monitor; np < End_monitor; np++) {
+ cgoto(np, y, STAT_SCAN_COL);
+ outch(np, c);
+ }
+# endif
+ for (np = Player; np < End_player; np++) {
+ cgoto(np, y, STAT_SCAN_COL);
+ outch(np, c);
+ }
+}
+
+/*
+ * drawplayer:
+ * Draw the player on the screen and show him to everyone who's scanning
+ * unless he is cloaked.
+ */
+drawplayer(pp, draw)
+PLAYER *pp;
+FLAG draw;
+{
+ register PLAYER *newp;
+ register int x, y;
+
+ x = pp->p_x;
+ y = pp->p_y;
+ Maze[y][x] = draw ? pp->p_face : pp->p_over;
+
+# ifdef MONITOR
+ for (newp = Monitor; newp < End_monitor; newp++)
+ check(newp, y, x);
+# endif
+
+ for (newp = Player; newp < End_player; newp++) {
+ if (!draw || newp == pp) {
+ check(newp, y, x);
+ continue;
+ }
+ if (newp->p_scan == 0) {
+ newp->p_scan--;
+ showstat(newp);
+ }
+ else if (newp->p_scan > 0) {
+ if (pp->p_cloak < 0)
+ check(newp, y, x);
+ newp->p_scan--;
+ }
+ }
+ if (!draw || pp->p_cloak < 0)
+ return;
+ if (pp->p_cloak-- == 0)
+ showstat(pp);
+}
+
+message(pp, s)
+register PLAYER *pp;
+char *s;
+{
+ cgoto(pp, HEIGHT, 0);
+ outstr(pp, s, strlen(s));
+ ce(pp);
+}
+
+/*
+ * translate:
+ * Turn a character into the right direction character if we are
+ * looking at the current player.
+ */
+translate(ch)
+char ch;
+{
+ switch (ch) {
+ case LEFTS:
+ return '<';
+ case RIGHT:
+ return '>';
+ case ABOVE:
+ return '^';
+ case BELOW:
+ return 'v';
+ }
+ return ch;
+}
+
+/*
+ * player_sym:
+ * Return the player symbol
+ */
+player_sym(pp, y, x)
+PLAYER *pp;
+int y, x;
+{
+ register PLAYER *npp;
+
+ npp = play_at(y, x);
+ if (npp->p_ident->i_team == ' ')
+ return Maze[y][x];
+#ifdef MONITOR
+ if (pp->p_ident->i_team == '*')
+ return npp->p_ident->i_team;
+#endif
+ if (pp->p_ident->i_team != npp->p_ident->i_team)
+ return Maze[y][x];
+ return pp->p_ident->i_team;
+}
diff --git a/hunt/huntd/driver.c b/hunt/huntd/driver.c
new file mode 100644
index 00000000..f7f0c9d6
--- /dev/null
+++ b/hunt/huntd/driver.c
@@ -0,0 +1,956 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+# include <signal.h>
+# include <errno.h>
+# include <sys/ioctl.h>
+# include <sys/time.h>
+
+# ifndef pdp11
+# define RN (((Seed = Seed * 11109 + 13849) >> 16) & 0xffff)
+# else
+# define RN ((Seed = Seed * 11109 + 13849) & 0x7fff)
+# endif
+
+int Seed = 0;
+
+
+SOCKET Daemon;
+char *First_arg; /* pointer to argv[0] */
+char *Last_arg; /* pointer to end of argv/environ */
+# ifdef INTERNET
+int Test_socket; /* test socket to answer datagrams */
+FLAG inetd_spawned; /* invoked via inetd */
+FLAG standard_port = TRUE; /* true if listening on standard port */
+u_short sock_port; /* port # of tcp listen socket */
+u_short stat_port; /* port # of statistics tcp socket */
+# define DAEMON_SIZE (sizeof Daemon)
+# else
+# define DAEMON_SIZE (sizeof Daemon - 1)
+# endif
+
+extern SIGNAL_TYPE cleanup();
+
+/*
+ * main:
+ * The main program.
+ */
+main(ac, av, ep)
+int ac;
+char **av, **ep;
+{
+ register PLAYER *pp;
+ register int had_char;
+# ifdef INTERNET
+ register long test_mask;
+ u_short msg;
+ short port_num, reply;
+ int namelen;
+ SOCKET test;
+# endif
+ static long read_fds;
+ static FLAG first = TRUE;
+ static FLAG server = FALSE;
+ extern int optind;
+ extern char *optarg;
+ int c;
+ static struct timeval linger = { 90, 0 };
+
+ First_arg = av[0];
+ if (ep == NULL || *ep == NULL)
+ ep = av + ac;
+ while (*ep)
+ ep++;
+ Last_arg = ep[-1] + strlen(ep[-1]);
+
+ while ((c = getopt(ac, av, "sp:")) != EOF) {
+ switch (c) {
+ case 's':
+ server = TRUE;
+ break;
+# ifdef INTERNET
+ case 'p':
+ standard_port = FALSE;
+ Test_port = atoi(optarg);
+ break;
+# endif
+ default:
+erred:
+ fprintf(stderr, "Usage: %s [-s] [-p port]\n", av[0]);
+ exit(1);
+ }
+ }
+ if (optind < ac)
+ goto erred;
+
+ init();
+ Sock_mask = (1 << Socket);
+ Stat_mask = (1 << Status);
+# ifdef INTERNET
+ test_mask = (1 << Test_socket);
+# endif
+
+
+again:
+ do {
+ read_fds = Fds_mask;
+ errno = 0;
+ while (select(Num_fds, &read_fds, (int *) NULL,
+ (int *) NULL, (struct timeval *) NULL) < 0)
+ {
+ if (errno != EINTR)
+# ifdef LOG
+ syslog(LOG_WARNING, "select: %m");
+# else
+ perror("select");
+# endif
+ errno = 0;
+ }
+ Have_inp = read_fds;
+# ifdef INTERNET
+ if (read_fds & test_mask) {
+ namelen = DAEMON_SIZE;
+ port_num = htons(sock_port);
+ (void) recvfrom(Test_socket, (char *) &msg, sizeof msg,
+ 0, (struct sockaddr *) &test, &namelen);
+ switch (ntohs(msg)) {
+ case C_MESSAGE:
+ if (Nplayer <= 0)
+ break;
+ reply = htons((u_short) Nplayer);
+ (void) sendto(Test_socket, (char *) &reply,
+ sizeof reply, 0,
+ (struct sockaddr *) &test, DAEMON_SIZE);
+ break;
+ case C_SCORES:
+ reply = htons(stat_port);
+ (void) sendto(Test_socket, (char *) &reply,
+ sizeof reply, 0,
+ (struct sockaddr *) &test, DAEMON_SIZE);
+ break;
+ case C_PLAYER:
+ case C_MONITOR:
+ if (msg == C_MONITOR && Nplayer <= 0)
+ break;
+ reply = htons(sock_port);
+ (void) sendto(Test_socket, (char *) &reply,
+ sizeof reply, 0,
+ (struct sockaddr *) &test, DAEMON_SIZE);
+ break;
+ }
+ }
+# endif
+ for (;;) {
+ had_char = FALSE;
+ for (pp = Player; pp < End_player; pp++)
+ if (havechar(pp)) {
+ execute(pp);
+ pp->p_nexec++;
+ had_char++;
+ }
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ if (havechar(pp)) {
+ mon_execute(pp);
+ pp->p_nexec++;
+ had_char++;
+ }
+# endif
+ if (!had_char)
+ break;
+ moveshots();
+ for (pp = Player; pp < End_player; )
+ if (pp->p_death[0] != '\0')
+ zap(pp, TRUE);
+ else
+ pp++;
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; )
+ if (pp->p_death[0] != '\0')
+ zap(pp, FALSE);
+ else
+ pp++;
+# endif
+ }
+ if (read_fds & Sock_mask)
+ if (answer()) {
+# ifdef INTERNET
+ if (first && standard_port)
+ faketalk();
+# endif
+ first = FALSE;
+ }
+ if (read_fds & Stat_mask)
+ send_stats();
+ for (pp = Player; pp < End_player; pp++) {
+ if (read_fds & pp->p_mask)
+ sendcom(pp, READY, pp->p_nexec);
+ pp->p_nexec = 0;
+ (void) fflush(pp->p_output);
+ }
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++) {
+ if (read_fds & pp->p_mask)
+ sendcom(pp, READY, pp->p_nexec);
+ pp->p_nexec = 0;
+ (void) fflush(pp->p_output);
+ }
+# endif
+ } while (Nplayer > 0);
+
+ read_fds = Fds_mask;
+ if (select(Num_fds, &read_fds, (int *) NULL, (int *) NULL,
+ &linger) > 0) {
+ goto again;
+ }
+ if (server) {
+ clear_scores();
+ makemaze();
+ clearwalls();
+# ifdef BOOTS
+ makeboots();
+# endif
+ first = TRUE;
+ goto again;
+ }
+
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; )
+ zap(pp, FALSE);
+# endif
+ cleanup(0);
+}
+
+/*
+ * init:
+ * Initialize the global parameters.
+ */
+init()
+{
+ register int i;
+# ifdef INTERNET
+ auto SOCKET test_port;
+ auto int msg;
+ auto int len;
+# endif
+
+# ifndef DEBUG
+# ifdef TIOCNOTTY
+ (void) ioctl(fileno(stdout), TIOCNOTTY, NULL);
+# endif
+ (void) setpgrp(getpid(), getpid());
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGQUIT, SIG_IGN);
+ (void) signal(SIGTERM, cleanup);
+# endif
+
+ (void) chdir("/usr/tmp"); /* just in case it core dumps */
+ (void) umask(0); /* No privacy at all! */
+ (void) signal(SIGPIPE, SIG_IGN);
+
+# ifdef LOG
+# ifdef SYSLOG_43
+ openlog("HUNT", LOG_PID, LOG_DAEMON);
+# endif
+# ifdef SYSLOG_42
+ openlog("HUNT", LOG_PID);
+# endif
+# endif
+
+ /*
+ * Initialize statistics socket
+ */
+# ifdef INTERNET
+ Daemon.sin_family = SOCK_FAMILY;
+ Daemon.sin_addr.s_addr = INADDR_ANY;
+ Daemon.sin_port = 0;
+# else
+ Daemon.sun_family = SOCK_FAMILY;
+ (void) strcpy(Daemon.sun_path, Stat_name);
+# endif
+
+ Status = socket(SOCK_FAMILY, SOCK_STREAM, 0);
+ if (bind(Status, (struct sockaddr *) &Daemon, DAEMON_SIZE) < 0) {
+ if (errno == EADDRINUSE)
+ exit(0);
+ else {
+# ifdef LOG
+ syslog(LOG_ERR, "bind: %m");
+# else
+ perror("bind");
+# endif
+ cleanup(1);
+ }
+ }
+ (void) listen(Status, 5);
+
+# ifdef INTERNET
+ len = sizeof (SOCKET);
+ if (getsockname(Status, (struct sockaddr *) &Daemon, &len) < 0) {
+# ifdef LOG
+ syslog(LOG_ERR, "getsockname: %m");
+# else
+ perror("getsockname");
+# endif
+ exit(1);
+ }
+ stat_port = ntohs(Daemon.sin_port);
+# endif
+
+ /*
+ * Initialize main socket
+ */
+# ifdef INTERNET
+ Daemon.sin_family = SOCK_FAMILY;
+ Daemon.sin_addr.s_addr = INADDR_ANY;
+ Daemon.sin_port = 0;
+# else
+ Daemon.sun_family = SOCK_FAMILY;
+ (void) strcpy(Daemon.sun_path, Sock_name);
+# endif
+
+ Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
+# if defined(INTERNET)
+ msg = 1;
+ if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK, &msg, sizeof msg)<0)
+# ifdef LOG
+ syslog(LOG_WARNING, "setsockopt loopback %m");
+# else
+ perror("setsockopt loopback");
+# endif
+# endif
+ if (bind(Socket, (struct sockaddr *) &Daemon, DAEMON_SIZE) < 0) {
+ if (errno == EADDRINUSE)
+ exit(0);
+ else {
+# ifdef LOG
+ syslog(LOG_ERR, "bind: %m");
+# else
+ perror("bind");
+# endif
+ cleanup(1);
+ }
+ }
+ (void) listen(Socket, 5);
+
+# ifdef INTERNET
+ len = sizeof (SOCKET);
+ if (getsockname(Socket, (struct sockaddr *) &Daemon, &len) < 0) {
+# ifdef LOG
+ syslog(LOG_ERR, "getsockname: %m");
+# else
+ perror("getsockname");
+# endif
+ exit(1);
+ }
+ sock_port = ntohs(Daemon.sin_port);
+# endif
+
+ /*
+ * Initialize minimal select mask
+ */
+ Fds_mask = (1 << Socket) | (1 << Status);
+ Num_fds = ((Socket > Status) ? Socket : Status) + 1;
+
+# ifdef INTERNET
+ len = sizeof (SOCKET);
+ if (getsockname(0, (struct sockaddr *) &test_port, &len) >= 0
+ && test_port.sin_family == AF_INET) {
+ inetd_spawned = TRUE;
+ Test_socket = 0;
+ if (test_port.sin_port != htons((u_short) Test_port)) {
+ standard_port = FALSE;
+ Test_port = ntohs(test_port.sin_port);
+ }
+ } else {
+ test_port = Daemon;
+ test_port.sin_port = htons((u_short) Test_port);
+
+ Test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
+ if (bind(Test_socket, (struct sockaddr *) &test_port,
+ DAEMON_SIZE) < 0) {
+# ifdef LOG
+ syslog(LOG_ERR, "bind: %m");
+# else
+ perror("bind");
+# endif
+ exit(1);
+ }
+ (void) listen(Test_socket, 5);
+ }
+
+ Fds_mask |= (1 << Test_socket);
+ if (Test_socket + 1 > Num_fds)
+ Num_fds = Test_socket + 1;
+# endif
+
+ Seed = getpid() + time((time_t *) NULL);
+ makemaze();
+# ifdef BOOTS
+ makeboots();
+# endif
+
+ for (i = 0; i < NASCII; i++)
+ See_over[i] = TRUE;
+ See_over[DOOR] = FALSE;
+ See_over[WALL1] = FALSE;
+ See_over[WALL2] = FALSE;
+ See_over[WALL3] = FALSE;
+# ifdef REFLECT
+ See_over[WALL4] = FALSE;
+ See_over[WALL5] = FALSE;
+# endif
+
+}
+
+# ifdef BOOTS
+/*
+ * makeboots:
+ * Put the boots in the maze
+ */
+makeboots()
+{
+ register int x, y;
+ register PLAYER *pp;
+
+ do {
+ x = rand_num(WIDTH - 1) + 1;
+ y = rand_num(HEIGHT - 1) + 1;
+ } while (Maze[y][x] != SPACE);
+ Maze[y][x] = BOOT_PAIR;
+ for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
+ pp->p_flying = -1;
+}
+# endif
+
+
+/*
+ * checkdam:
+ * Check the damage to the given player, and see if s/he is killed
+ */
+checkdam(ouch, gotcha, credit, amt, shot_type)
+register PLAYER *ouch, *gotcha;
+register IDENT *credit;
+int amt;
+char shot_type;
+{
+ register char *cp;
+
+ if (ouch->p_death[0] != '\0')
+ return;
+# ifdef BOOTS
+ if (shot_type == SLIME)
+ switch (ouch->p_nboots) {
+ default:
+ break;
+ case 1:
+ amt = (amt + 1) / 2;
+ break;
+ case 2:
+ if (gotcha != NULL)
+ message(gotcha, "He has boots on!");
+ return;
+ }
+# endif
+ ouch->p_damage += amt;
+ if (ouch->p_damage <= ouch->p_damcap) {
+ (void) sprintf(Buf, "%2d", ouch->p_damage);
+ cgoto(ouch, STAT_DAM_ROW, STAT_VALUE_COL);
+ outstr(ouch, Buf, 2);
+ return;
+ }
+
+ /* Someone DIED */
+ switch (shot_type) {
+ default:
+ cp = "Killed";
+ break;
+# ifdef FLY
+ case FALL:
+ cp = "Killed on impact";
+ break;
+# endif
+ case KNIFE:
+ cp = "Stabbed to death";
+ ouch->p_ammo = 0; /* No exploding */
+ break;
+ case SHOT:
+ cp = "Shot to death";
+ break;
+ case GRENADE:
+ case SATCHEL:
+ case BOMB:
+ cp = "Bombed";
+ break;
+ case MINE:
+ case GMINE:
+ cp = "Blown apart";
+ break;
+# ifdef OOZE
+ case SLIME:
+ cp = "Slimed";
+ if (credit != NULL)
+ credit->i_slime++;
+ break;
+# endif
+# ifdef VOLCANO
+ case LAVA:
+ cp = "Baked";
+ break;
+# endif
+# ifdef DRONE
+ case DSHOT:
+ cp = "Eliminated";
+ break;
+# endif
+ }
+ if (credit == NULL) {
+ (void) sprintf(ouch->p_death, "| %s by %s |", cp,
+ (shot_type == MINE || shot_type == GMINE) ?
+ "a mine" : "act of God");
+ return;
+ }
+
+ (void) sprintf(ouch->p_death, "| %s by %s |", cp, credit->i_name);
+
+ if (ouch == gotcha) { /* No use killing yourself */
+ credit->i_kills--;
+ credit->i_bkills++;
+ }
+ else if (ouch->p_ident->i_team == ' '
+ || ouch->p_ident->i_team != credit->i_team) {
+ credit->i_kills++;
+ credit->i_gkills++;
+ }
+ else {
+ credit->i_kills--;
+ credit->i_bkills++;
+ }
+ credit->i_score = credit->i_kills / (double) credit->i_entries;
+ ouch->p_ident->i_deaths++;
+ if (ouch->p_nchar == 0)
+ ouch->p_ident->i_stillb++;
+ if (gotcha == NULL)
+ return;
+ gotcha->p_damcap += STABDAM;
+ gotcha->p_damage -= STABDAM;
+ if (gotcha->p_damage < 0)
+ gotcha->p_damage = 0;
+ (void) sprintf(Buf, "%2d/%2d", gotcha->p_damage, gotcha->p_damcap);
+ cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL);
+ outstr(gotcha, Buf, 5);
+ (void) sprintf(Buf, "%3d", (gotcha->p_damcap - MAXDAM) / 2);
+ cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL);
+ outstr(gotcha, Buf, 3);
+ (void) sprintf(Buf, "%5.2f", gotcha->p_ident->i_score);
+ for (ouch = Player; ouch < End_player; ouch++) {
+ cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
+ STAT_NAME_COL);
+ outstr(ouch, Buf, 5);
+ }
+# ifdef MONITOR
+ for (ouch = Monitor; ouch < End_monitor; ouch++) {
+ cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
+ STAT_NAME_COL);
+ outstr(ouch, Buf, 5);
+ }
+# endif
+}
+
+/*
+ * zap:
+ * Kill off a player and take him out of the game.
+ */
+zap(pp, was_player)
+register PLAYER *pp;
+FLAG was_player;
+{
+ register int i, len;
+ register BULLET *bp;
+ register PLAYER *np;
+ register int x, y;
+ int savefd, savemask;
+
+ if (was_player) {
+ if (pp->p_undershot)
+ fixshots(pp->p_y, pp->p_x, pp->p_over);
+ drawplayer(pp, FALSE);
+ Nplayer--;
+ }
+
+ len = strlen(pp->p_death); /* Display the cause of death */
+ x = (WIDTH - len) / 2;
+ cgoto(pp, HEIGHT / 2, x);
+ outstr(pp, pp->p_death, len);
+ for (i = 1; i < len; i++)
+ pp->p_death[i] = '-';
+ pp->p_death[0] = '+';
+ pp->p_death[len - 1] = '+';
+ cgoto(pp, HEIGHT / 2 - 1, x);
+ outstr(pp, pp->p_death, len);
+ cgoto(pp, HEIGHT / 2 + 1, x);
+ outstr(pp, pp->p_death, len);
+ cgoto(pp, HEIGHT, 0);
+
+ savefd = pp->p_fd;
+ savemask = pp->p_mask;
+
+# ifdef MONITOR
+ if (was_player) {
+# endif
+ for (bp = Bullets; bp != NULL; bp = bp->b_next) {
+ if (bp->b_owner == pp)
+ bp->b_owner = NULL;
+ if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
+ bp->b_over = SPACE;
+ }
+
+ i = rand_num(pp->p_ammo);
+ x = rand_num(pp->p_ammo);
+ if (x > i)
+ i = x;
+ if (pp->p_ammo == 0)
+ x = 0;
+ else if (i == pp->p_ammo - 1) {
+ x = pp->p_ammo;
+ len = SLIME;
+ }
+ else {
+ for (x = MAXBOMB - 1; x > 0; x--)
+ if (i >= shot_req[x])
+ break;
+ for (y = MAXSLIME - 1; y > 0; y--)
+ if (i >= slime_req[y])
+ break;
+ if (y >= 0 && slime_req[y] > shot_req[x]) {
+ x = slime_req[y];
+ len = SLIME;
+ }
+ else if (x != 0) {
+ len = shot_type[x];
+ x = shot_req[x];
+ }
+ }
+ if (x > 0) {
+ (void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x,
+ (PLAYER *) NULL, TRUE, SPACE);
+ (void) sprintf(Buf, "%s detonated.",
+ pp->p_ident->i_name);
+ for (np = Player; np < End_player; np++)
+ message(np, Buf);
+# ifdef MONITOR
+ for (np = Monitor; np < End_monitor; np++)
+ message(np, Buf);
+# endif
+# ifdef BOOTS
+ while (pp->p_nboots-- > 0) {
+ for (np = Boot; np < &Boot[NBOOTS]; np++)
+ if (np->p_flying < 0)
+ break;
+ if (np >= &Boot[NBOOTS])
+ abort(1, "Too many boots");
+ np->p_undershot = FALSE;
+ np->p_x = pp->p_x;
+ np->p_y = pp->p_y;
+ np->p_flying = rand_num(20);
+ np->p_flyx = 2 * rand_num(6) - 5;
+ np->p_flyy = 2 * rand_num(6) - 5;
+ np->p_over = SPACE;
+ np->p_face = BOOT;
+ showexpl(np->p_y, np->p_x, BOOT);
+ }
+# endif
+ }
+# ifdef BOOTS
+ else if (pp->p_nboots > 0) {
+ if (pp->p_nboots == 2)
+ Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
+ else
+ Maze[pp->p_y][pp->p_x] = BOOT;
+ if (pp->p_undershot)
+ fixshots(pp->p_y, pp->p_x,
+ Maze[pp->p_y][pp->p_x]);
+ }
+# endif
+
+# ifdef VOLCANO
+ volcano += pp->p_ammo - x;
+ if (rand_num(100) < volcano / 50) {
+ do {
+ x = rand_num(WIDTH / 2) + WIDTH / 4;
+ y = rand_num(HEIGHT / 2) + HEIGHT / 4;
+ } while (Maze[y][x] != SPACE);
+ (void) add_shot(LAVA, y, x, LEFTS, volcano,
+ (PLAYER *) NULL, TRUE, SPACE);
+ for (np = Player; np < End_player; np++)
+ message(np, "Volcano eruption.");
+ volcano = 0;
+ }
+# endif
+
+# ifdef DRONE
+ if (rand_num(100) < 2) {
+ do {
+ x = rand_num(WIDTH / 2) + WIDTH / 4;
+ y = rand_num(HEIGHT / 2) + HEIGHT / 4;
+ } while (Maze[y][x] != SPACE);
+ add_shot(DSHOT, y, x, rand_dir(),
+ shot_req[MINDSHOT +
+ rand_num(MAXBOMB - MINDSHOT)],
+ (PLAYER *) NULL, FALSE, SPACE);
+ }
+# endif
+
+ sendcom(pp, ENDWIN);
+ (void) putc(' ', pp->p_output);
+ (void) fclose(pp->p_output);
+
+ End_player--;
+ if (pp != End_player) {
+ memcpy(pp, End_player, sizeof (PLAYER));
+ (void) sprintf(Buf, "%5.2f%c%-10.10s %c",
+ pp->p_ident->i_score, stat_char(pp),
+ pp->p_ident->i_name, pp->p_ident->i_team);
+ i = STAT_PLAY_ROW + 1 + (pp - Player);
+ for (np = Player; np < End_player; np++) {
+ cgoto(np, i, STAT_NAME_COL);
+ outstr(np, Buf, STAT_NAME_LEN);
+ }
+# ifdef MONITOR
+ for (np = Monitor; np < End_monitor; np++) {
+ cgoto(np, i, STAT_NAME_COL);
+ outstr(np, Buf, STAT_NAME_LEN);
+ }
+# endif
+ }
+
+ /* Erase the last player */
+ i = STAT_PLAY_ROW + 1 + Nplayer;
+ for (np = Player; np < End_player; np++) {
+ cgoto(np, i, STAT_NAME_COL);
+ ce(np);
+ }
+# ifdef MONITOR
+ for (np = Monitor; np < End_monitor; np++) {
+ cgoto(np, i, STAT_NAME_COL);
+ ce(np);
+ }
+ }
+ else {
+ sendcom(pp, ENDWIN);
+ (void) putc(LAST_PLAYER, pp->p_output);
+ (void) fclose(pp->p_output);
+
+ End_monitor--;
+ if (pp != End_monitor) {
+ memcpy(pp, End_monitor, sizeof (PLAYER));
+ (void) sprintf(Buf, "%5.5s %-10.10s %c", " ",
+ pp->p_ident->i_name, pp->p_ident->i_team);
+ i = STAT_MON_ROW + 1 + (pp - Player);
+ for (np = Player; np < End_player; np++) {
+ cgoto(np, i, STAT_NAME_COL);
+ outstr(np, Buf, STAT_NAME_LEN);
+ }
+ for (np = Monitor; np < End_monitor; np++) {
+ cgoto(np, i, STAT_NAME_COL);
+ outstr(np, Buf, STAT_NAME_LEN);
+ }
+ }
+
+ /* Erase the last monitor */
+ i = STAT_MON_ROW + 1 + (End_monitor - Monitor);
+ for (np = Player; np < End_player; np++) {
+ cgoto(np, i, STAT_NAME_COL);
+ ce(np);
+ }
+ for (np = Monitor; np < End_monitor; np++) {
+ cgoto(np, i, STAT_NAME_COL);
+ ce(np);
+ }
+
+ }
+# endif
+
+ Fds_mask &= ~savemask;
+ if (Num_fds == savefd + 1) {
+ Num_fds = Socket;
+# ifdef INTERNET
+ if (Test_socket > Socket)
+ Num_fds = Test_socket;
+# endif
+ for (np = Player; np < End_player; np++)
+ if (np->p_fd > Num_fds)
+ Num_fds = np->p_fd;
+# ifdef MONITOR
+ for (np = Monitor; np < End_monitor; np++)
+ if (np->p_fd > Num_fds)
+ Num_fds = np->p_fd;
+# endif
+ Num_fds++;
+ }
+}
+
+/*
+ * rand_num:
+ * Return a random number in a given range.
+ */
+rand_num(range)
+int range;
+{
+ return (range == 0 ? 0 : RN % range);
+}
+
+/*
+ * havechar:
+ * Check to see if we have any characters in the input queue; if
+ * we do, read them, stash them away, and return TRUE; else return
+ * FALSE.
+ */
+havechar(pp)
+register PLAYER *pp;
+{
+ extern int errno;
+
+ if (pp->p_ncount < pp->p_nchar)
+ return TRUE;
+ if (!(Have_inp & pp->p_mask))
+ return FALSE;
+ Have_inp &= ~pp->p_mask;
+check_again:
+ errno = 0;
+ if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0)
+ {
+ if (errno == EINTR)
+ goto check_again;
+ pp->p_cbuf[0] = 'q';
+ }
+ pp->p_ncount = 0;
+ return TRUE;
+}
+
+/*
+ * cleanup:
+ * Exit with the given value, cleaning up any droppings lying around
+ */
+SIGNAL_TYPE
+cleanup(eval)
+int eval;
+{
+ register PLAYER *pp;
+
+ for (pp = Player; pp < End_player; pp++) {
+ cgoto(pp, HEIGHT, 0);
+ sendcom(pp, ENDWIN);
+ (void) putc(LAST_PLAYER, pp->p_output);
+ (void) fclose(pp->p_output);
+ }
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++) {
+ cgoto(pp, HEIGHT, 0);
+ sendcom(pp, ENDWIN);
+ (void) putc(LAST_PLAYER, pp->p_output);
+ (void) fclose(pp->p_output);
+ }
+# endif
+ (void) close(Socket);
+# ifdef AF_UNIX_HACK
+ (void) unlink(Sock_name);
+# endif
+
+ exit(eval);
+}
+
+/*
+ * send_stats:
+ * Print stats to requestor
+ */
+send_stats()
+{
+ register IDENT *ip;
+ register FILE *fp;
+ int s;
+ SOCKET sockstruct;
+ int socklen;
+
+ /*
+ * Get the output stream ready
+ */
+# ifdef INTERNET
+ socklen = sizeof sockstruct;
+# else
+ socklen = sizeof sockstruct - 1;
+# endif
+ s = accept(Status, (struct sockaddr *) &sockstruct, &socklen);
+ if (s < 0) {
+ if (errno == EINTR)
+ return;
+# ifdef LOG
+ syslog(LOG_ERR, "accept: %m");
+# else
+ perror("accept");
+# endif
+ return;
+ }
+ fp = fdopen(s, "w");
+ if (fp == NULL) {
+# ifdef LOG
+ syslog(LOG_ERR, "fdopen: %m");
+# else
+ perror("fdopen");
+# endif
+ (void) close(s);
+ return;
+ }
+
+ /*
+ * Send output to requestor
+ */
+ fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
+ for (ip = Scores; ip != NULL; ip = ip->i_next) {
+ fprintf(fp, "%s\t", ip->i_name);
+ if (strlen(ip->i_name) < 8)
+ putc('\t', fp);
+ fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ ip->i_score, ip->i_ducked, ip->i_absorbed,
+ ip->i_faced, ip->i_shot, ip->i_robbed,
+ ip->i_missed, ip->i_slime);
+ }
+ fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp);
+ for (ip = Scores; ip != NULL; ip = ip->i_next) {
+ if (ip->i_team == ' ') {
+ fprintf(fp, "%s\t", ip->i_name);
+ if (strlen(ip->i_name) < 8)
+ putc('\t', fp);
+ }
+ else {
+ fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team);
+ if (strlen(ip->i_name) + 3 < 8)
+ putc('\t', fp);
+ }
+ fprintf(fp, "%d\t%d\t%d\t%d\t%d\n",
+ ip->i_gkills, ip->i_bkills, ip->i_deaths,
+ ip->i_stillb, ip->i_saved);
+ }
+
+ (void) fclose(fp);
+}
+
+/*
+ * clear_scores:
+ * Clear out the scores so the next session start clean
+ */
+clear_scores()
+{
+ register IDENT *ip, *nextip;
+
+ for (ip = Scores; ip != NULL; ip = nextip) {
+ nextip = ip->i_next;
+ (void) free((char *) ip);
+ }
+ Scores = NULL;
+}
diff --git a/hunt/huntd/execute.c b/hunt/huntd/execute.c
new file mode 100644
index 00000000..c961d486
--- /dev/null
+++ b/hunt/huntd/execute.c
@@ -0,0 +1,556 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+
+# ifdef MONITOR
+/*
+ * mon_execute:
+ * Execute a single monitor command
+ */
+mon_execute(pp)
+register PLAYER *pp;
+{
+ register char ch;
+
+ ch = pp->p_cbuf[pp->p_ncount++];
+ switch (ch) {
+ case CTRL('L'):
+ sendcom(pp, REDRAW);
+ break;
+ case 'q':
+ (void) strcpy(pp->p_death, "| Quit |");
+ break;
+ }
+}
+# endif
+
+/*
+ * execute:
+ * Execute a single command
+ */
+execute(pp)
+register PLAYER *pp;
+{
+ register char ch;
+
+ ch = pp->p_cbuf[pp->p_ncount++];
+
+# ifdef FLY
+ if (pp->p_flying >= 0) {
+ switch (ch) {
+ case CTRL('L'):
+ sendcom(pp, REDRAW);
+ break;
+ case 'q':
+ (void) strcpy(pp->p_death, "| Quit |");
+ break;
+ }
+ return;
+ }
+# endif
+
+ switch (ch) {
+ case CTRL('L'):
+ sendcom(pp, REDRAW);
+ break;
+ case 'h':
+ move_player(pp, LEFTS);
+ break;
+ case 'H':
+ face(pp, LEFTS);
+ break;
+ case 'j':
+ move_player(pp, BELOW);
+ break;
+ case 'J':
+ face(pp, BELOW);
+ break;
+ case 'k':
+ move_player(pp, ABOVE);
+ break;
+ case 'K':
+ face(pp, ABOVE);
+ break;
+ case 'l':
+ move_player(pp, RIGHT);
+ break;
+ case 'L':
+ face(pp, RIGHT);
+ break;
+ case 'f':
+ case '1':
+ fire(pp, 0); /* SHOT */
+ break;
+ case 'g':
+ case '2':
+ fire(pp, 1); /* GRENADE */
+ break;
+ case 'F':
+ case '3':
+ fire(pp, 2); /* SATCHEL */
+ break;
+ case 'G':
+ case '4':
+ fire(pp, 3); /* 7x7 BOMB */
+ break;
+ case '5':
+ fire(pp, 4); /* 9x9 BOMB */
+ break;
+ case '6':
+ fire(pp, 5); /* 11x11 BOMB */
+ break;
+ case '7':
+ fire(pp, 6); /* 13x13 BOMB */
+ break;
+ case '8':
+ fire(pp, 7); /* 15x15 BOMB */
+ break;
+ case '9':
+ fire(pp, 8); /* 17x17 BOMB */
+ break;
+ case '0':
+ fire(pp, 9); /* 19x19 BOMB */
+ break;
+ case '@':
+ fire(pp, 10); /* 21x21 BOMB */
+ break;
+# ifdef OOZE
+ case 'o':
+ fire_slime(pp, 0); /* SLIME */
+ break;
+ case 'O':
+ fire_slime(pp, 1); /* SSLIME */
+ break;
+ case 'p':
+ fire_slime(pp, 2);
+ break;
+ case 'P':
+ fire_slime(pp, 3);
+ break;
+# endif
+ case 's':
+ scan(pp);
+ break;
+ case 'c':
+ cloak(pp);
+ break;
+ case 'q':
+ (void) strcpy(pp->p_death, "| Quit |");
+ break;
+ }
+}
+
+/*
+ * move_player:
+ * Execute a move in the given direction
+ */
+move_player(pp, dir)
+register PLAYER *pp;
+int dir;
+{
+ register PLAYER *newp;
+ register int x, y;
+ register FLAG moved;
+ register BULLET *bp;
+
+ y = pp->p_y;
+ x = pp->p_x;
+
+ switch (dir) {
+ case LEFTS:
+ x--;
+ break;
+ case RIGHT:
+ x++;
+ break;
+ case ABOVE:
+ y--;
+ break;
+ case BELOW:
+ y++;
+ break;
+ }
+
+ moved = FALSE;
+ switch (Maze[y][x]) {
+ case SPACE:
+# ifdef RANDOM
+ case DOOR:
+# endif
+ moved = TRUE;
+ break;
+ case WALL1:
+ case WALL2:
+ case WALL3:
+# ifdef REFLECT
+ case WALL4:
+ case WALL5:
+# endif
+ break;
+ case MINE:
+ case GMINE:
+ if (dir == pp->p_face)
+ pickup(pp, y, x, 2, Maze[y][x]);
+ else if (opposite(dir, pp->p_face))
+ pickup(pp, y, x, 95, Maze[y][x]);
+ else
+ pickup(pp, y, x, 50, Maze[y][x]);
+ Maze[y][x] = SPACE;
+ moved = TRUE;
+ break;
+ case SHOT:
+ case GRENADE:
+ case SATCHEL:
+ case BOMB:
+# ifdef OOZE
+ case SLIME:
+# endif
+# ifdef DRONE
+ case DSHOT:
+# endif
+ bp = is_bullet(y, x);
+ if (bp != NULL)
+ bp->b_expl = TRUE;
+ Maze[y][x] = SPACE;
+ moved = TRUE;
+ break;
+ case LEFTS:
+ case RIGHT:
+ case ABOVE:
+ case BELOW:
+ if (dir != pp->p_face)
+ sendcom(pp, BELL);
+ else {
+ newp = play_at(y, x);
+ checkdam(newp, pp, pp->p_ident, STABDAM, KNIFE);
+ }
+ break;
+# ifdef FLY
+ case FLYER:
+ newp = play_at(y, x);
+ message(newp, "Oooh, there's a short guy waving at you!");
+ message(pp, "You couldn't quite reach him!");
+ break;
+# endif
+# ifdef BOOTS
+ case BOOT:
+ case BOOT_PAIR:
+ if (Maze[y][x] == BOOT)
+ pp->p_nboots++;
+ else
+ pp->p_nboots += 2;
+ for (newp = Boot; newp < &Boot[NBOOTS]; newp++) {
+ if (newp->p_flying < 0)
+ continue;
+ if (newp->p_y == y && newp->p_x == x) {
+ newp->p_flying = -1;
+ if (newp->p_undershot)
+ fixshots(y, x, newp->p_over);
+ }
+ }
+ if (pp->p_nboots == 2)
+ message(pp, "Wow! A pair of boots!");
+ else
+ message(pp, "You can hobble around on one boot.");
+ Maze[y][x] = SPACE;
+ moved = TRUE;
+ break;
+# endif
+ }
+ if (moved) {
+ if (pp->p_ncshot > 0)
+ if (--pp->p_ncshot == MAXNCSHOT) {
+ cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
+ outstr(pp, " ok", 3);
+ }
+ if (pp->p_undershot) {
+ fixshots(pp->p_y, pp->p_x, pp->p_over);
+ pp->p_undershot = FALSE;
+ }
+ drawplayer(pp, FALSE);
+ pp->p_over = Maze[y][x];
+ pp->p_y = y;
+ pp->p_x = x;
+ drawplayer(pp, TRUE);
+ }
+}
+
+/*
+ * face:
+ * Change the direction the player is facing
+ */
+face(pp, dir)
+register PLAYER *pp;
+register int dir;
+{
+ if (pp->p_face != dir) {
+ pp->p_face = dir;
+ drawplayer(pp, TRUE);
+ }
+}
+
+/*
+ * fire:
+ * Fire a shot of the given type in the given direction
+ */
+fire(pp, req_index)
+register PLAYER *pp;
+register int req_index;
+{
+ if (pp == NULL)
+ return;
+# ifdef DEBUG
+ if (req_index < 0 || req_index >= MAXBOMB)
+ message(pp, "What you do?");
+# endif
+ while (req_index >= 0 && pp->p_ammo < shot_req[req_index])
+ req_index--;
+ if (req_index < 0) {
+ message(pp, "Not enough charges.");
+ return;
+ }
+ if (pp->p_ncshot > MAXNCSHOT)
+ return;
+ if (pp->p_ncshot++ == MAXNCSHOT) {
+ cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
+ outstr(pp, " ", 3);
+ }
+ pp->p_ammo -= shot_req[req_index];
+ (void) sprintf(Buf, "%3d", pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+
+ add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face,
+ shot_req[req_index], pp, FALSE, pp->p_face);
+ pp->p_undershot = TRUE;
+
+ /*
+ * Show the object to everyone
+ */
+ showexpl(pp->p_y, pp->p_x, shot_type[req_index]);
+ for (pp = Player; pp < End_player; pp++)
+ sendcom(pp, REFRESH);
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ sendcom(pp, REFRESH);
+# endif
+}
+
+# ifdef OOZE
+/*
+ * fire_slime:
+ * Fire a slime shot in the given direction
+ */
+fire_slime(pp, req_index)
+register PLAYER *pp;
+register int req_index;
+{
+ if (pp == NULL)
+ return;
+# ifdef DEBUG
+ if (req_index < 0 || req_index >= MAXSLIME)
+ message(pp, "What you do?");
+# endif
+ while (req_index >= 0 && pp->p_ammo < slime_req[req_index])
+ req_index--;
+ if (req_index < 0) {
+ message(pp, "Not enough charges.");
+ return;
+ }
+ if (pp->p_ncshot > MAXNCSHOT)
+ return;
+ if (pp->p_ncshot++ == MAXNCSHOT) {
+ cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
+ outstr(pp, " ", 3);
+ }
+ pp->p_ammo -= slime_req[req_index];
+ (void) sprintf(Buf, "%3d", pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+
+ add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face,
+ slime_req[req_index] * SLIME_FACTOR, pp, FALSE, pp->p_face);
+ pp->p_undershot = TRUE;
+
+ /*
+ * Show the object to everyone
+ */
+ showexpl(pp->p_y, pp->p_x, SLIME);
+ for (pp = Player; pp < End_player; pp++)
+ sendcom(pp, REFRESH);
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ sendcom(pp, REFRESH);
+# endif
+}
+# endif
+
+/*
+ * add_shot:
+ * Create a shot with the given properties
+ */
+add_shot(type, y, x, face, charge, owner, expl, over)
+int type;
+int y, x;
+char face;
+int charge;
+PLAYER *owner;
+int expl;
+char over;
+{
+ register BULLET *bp;
+ register int size;
+
+ switch (type) {
+ case SHOT:
+ case MINE:
+ size = 1;
+ break;
+ case GRENADE:
+ case GMINE:
+ size = 2;
+ break;
+ case SATCHEL:
+ size = 3;
+ break;
+ case BOMB:
+ for (size = 3; size < MAXBOMB; size++)
+ if (shot_req[size] >= charge)
+ break;
+ size++;
+ break;
+ default:
+ size = 0;
+ break;
+ }
+
+ bp = create_shot(type, y, x, face, charge, size, owner,
+ (owner == NULL) ? NULL : owner->p_ident, expl, over);
+ bp->b_next = Bullets;
+ Bullets = bp;
+}
+
+BULLET *
+create_shot(type, y, x, face, charge, size, owner, score, expl, over)
+int type;
+int y, x;
+char face;
+int charge;
+int size;
+PLAYER *owner;
+IDENT *score;
+int expl;
+char over;
+{
+ register BULLET *bp;
+
+ bp = (BULLET *) malloc(sizeof (BULLET)); /* NOSTRICT */
+ if (bp == NULL) {
+ if (owner != NULL)
+ message(owner, "Out of memory");
+ return NULL;
+ }
+
+ bp->b_face = face;
+ bp->b_x = x;
+ bp->b_y = y;
+ bp->b_charge = charge;
+ bp->b_owner = owner;
+ bp->b_score = score;
+ bp->b_type = type;
+ bp->b_size = size;
+ bp->b_expl = expl;
+ bp->b_over = over;
+ bp->b_next = NULL;
+
+ return bp;
+}
+
+/*
+ * cloak:
+ * Turn on or increase length of a cloak
+ */
+cloak(pp)
+register PLAYER *pp;
+{
+ if (pp->p_ammo <= 0) {
+ message(pp, "No more charges");
+ return;
+ }
+# ifdef BOOTS
+ if (pp->p_nboots > 0) {
+ message(pp, "Boots are too noisy to cloak!");
+ return;
+ }
+# endif
+ (void) sprintf(Buf, "%3d", --pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+
+ pp->p_cloak += CLOAKLEN;
+
+ if (pp->p_scan >= 0)
+ pp->p_scan = -1;
+
+ showstat(pp);
+}
+
+/*
+ * scan:
+ * Turn on or increase length of a scan
+ */
+scan(pp)
+register PLAYER *pp;
+{
+ if (pp->p_ammo <= 0) {
+ message(pp, "No more charges");
+ return;
+ }
+ (void) sprintf(Buf, "%3d", --pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+
+ pp->p_scan += SCANLEN;
+
+ if (pp->p_cloak >= 0)
+ pp->p_cloak = -1;
+
+ showstat(pp);
+}
+
+/*
+ * pickup:
+ * check whether the object blew up or whether he picked it up
+ */
+pickup(pp, y, x, prob, obj)
+register PLAYER *pp;
+register int y, x;
+int prob;
+int obj;
+{
+ register int req;
+
+ switch (obj) {
+ case MINE:
+ req = BULREQ;
+ break;
+ case GMINE:
+ req = GRENREQ;
+ break;
+ default:
+ abort();
+ }
+ if (rand_num(100) < prob)
+ add_shot(obj, y, x, LEFTS, req, (PLAYER *) NULL,
+ TRUE, pp->p_face);
+ else {
+ pp->p_ammo += req;
+ (void) sprintf(Buf, "%3d", pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+ }
+}
diff --git a/hunt/huntd/expl.c b/hunt/huntd/expl.c
new file mode 100644
index 00000000..59f7eee2
--- /dev/null
+++ b/hunt/huntd/expl.c
@@ -0,0 +1,211 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+
+/*
+ * showexpl:
+ * Show the explosions as they currently are
+ */
+showexpl(y, x, type)
+register int y, x;
+char type;
+{
+ register PLAYER *pp;
+ register EXPL *ep;
+
+ if (y < 0 || y >= HEIGHT)
+ return;
+ if (x < 0 || x >= WIDTH)
+ return;
+ ep = (EXPL *) malloc(sizeof (EXPL)); /* NOSTRICT */
+ ep->e_y = y;
+ ep->e_x = x;
+ ep->e_char = type;
+ ep->e_next = NULL;
+ if (Last_expl == NULL)
+ Expl[0] = ep;
+ else
+ Last_expl->e_next = ep;
+ Last_expl = ep;
+ for (pp = Player; pp < End_player; pp++) {
+ if (pp->p_maze[y][x] == type)
+ continue;
+ pp->p_maze[y][x] = type;
+ cgoto(pp, y, x);
+ outch(pp, type);
+ }
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++) {
+ if (pp->p_maze[y][x] == type)
+ continue;
+ pp->p_maze[y][x] = type;
+ cgoto(pp, y, x);
+ outch(pp, type);
+ }
+# endif
+ switch (Maze[y][x]) {
+ case WALL1:
+ case WALL2:
+ case WALL3:
+# ifdef RANDOM
+ case DOOR:
+# endif
+# ifdef REFLECT
+ case WALL4:
+ case WALL5:
+# endif
+ if (y >= UBOUND && y < DBOUND && x >= LBOUND && x < RBOUND)
+ remove_wall(y, x);
+ break;
+ }
+}
+
+/*
+ * rollexpl:
+ * Roll the explosions over, so the next one in the list is at the
+ * top
+ */
+rollexpl()
+{
+ register EXPL *ep;
+ register PLAYER *pp;
+ register int y, x;
+ register char c;
+ register EXPL *nextep;
+
+ for (ep = Expl[EXPLEN - 1]; ep != NULL; ep = nextep) {
+ nextep = ep->e_next;
+ y = ep->e_y;
+ x = ep->e_x;
+ if (y < UBOUND || y >= DBOUND || x < LBOUND || x >= RBOUND)
+ c = Maze[y][x];
+ else
+ c = SPACE;
+ for (pp = Player; pp < End_player; pp++)
+ if (pp->p_maze[y][x] == ep->e_char) {
+ pp->p_maze[y][x] = c;
+ cgoto(pp, y, x);
+ outch(pp, c);
+ }
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, y, x);
+# endif
+ free((char *) ep);
+ }
+ for (x = EXPLEN - 1; x > 0; x--)
+ Expl[x] = Expl[x - 1];
+ Last_expl = Expl[0] = NULL;
+}
+
+/* There's about 700 walls in the initial maze. So we pick a number
+ * that keeps the maze relatively full. */
+# define MAXREMOVE 40
+
+static REGEN removed[MAXREMOVE];
+static REGEN *rem_index = removed;
+
+/*
+ * remove_wall - add a location where the wall was blown away.
+ * if there is no space left over, put the a wall at
+ * the location currently pointed at.
+ */
+remove_wall(y, x)
+int y, x;
+{
+ register REGEN *r;
+# if defined(MONITOR) || defined(FLY)
+ register PLAYER *pp;
+# endif
+# ifdef FLY
+ register char save_char;
+# endif
+
+ r = rem_index;
+ while (r->r_y != 0) {
+# ifdef FLY
+ switch (Maze[r->r_y][r->r_x]) {
+ case SPACE:
+ case LEFTS:
+ case RIGHT:
+ case ABOVE:
+ case BELOW:
+ case FLYER:
+ save_char = Maze[r->r_y][r->r_x];
+ goto found;
+ }
+# else
+ if (Maze[r->r_y][r->r_x] == SPACE)
+ break;
+# endif
+ if (++r >= &removed[MAXREMOVE])
+ r = removed;
+ }
+
+found:
+ if (r->r_y != 0) {
+ /* Slot being used, put back this wall */
+# ifdef FLY
+ if (save_char == SPACE)
+ Maze[r->r_y][r->r_x] = Orig_maze[r->r_y][r->r_x];
+ else {
+ pp = play_at(r->r_y, r->r_x);
+ if (pp->p_flying >= 0)
+ pp->p_flying += rand_num(10);
+ else {
+ pp->p_flying = rand_num(20);
+ pp->p_flyx = 2 * rand_num(6) - 5;
+ pp->p_flyy = 2 * rand_num(6) - 5;
+ }
+ pp->p_over = Orig_maze[r->r_y][r->r_x];
+ pp->p_face = FLYER;
+ Maze[r->r_y][r->r_x] = FLYER;
+ showexpl(r->r_y, r->r_x, FLYER);
+ }
+# else
+ Maze[r->r_y][r->r_x] = Orig_maze[r->r_y][r->r_x];
+# endif
+# ifdef RANDOM
+ if (rand_num(100) == 0)
+ Maze[r->r_y][r->r_x] = DOOR;
+# endif
+# ifdef REFLECT
+ if (rand_num(100) == 0) /* one percent of the time */
+ Maze[r->r_y][r->r_x] = WALL4;
+# endif
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, r->r_y, r->r_x);
+# endif
+ }
+
+ r->r_y = y;
+ r->r_x = x;
+ if (++r >= &removed[MAXREMOVE])
+ rem_index = removed;
+ else
+ rem_index = r;
+
+ Maze[y][x] = SPACE;
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, y, x);
+# endif
+}
+
+/*
+ * clearwalls:
+ * Clear out the walls array
+ */
+clearwalls()
+{
+ register REGEN *rp;
+
+ for (rp = removed; rp < &removed[MAXREMOVE]; rp++)
+ rp->r_y = 0;
+ rem_index = removed;
+}
diff --git a/hunt/huntd/extern.c b/hunt/huntd/extern.c
new file mode 100644
index 00000000..64e5fccf
--- /dev/null
+++ b/hunt/huntd/extern.c
@@ -0,0 +1,64 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+
+# ifdef MONITOR
+FLAG Am_monitor = FALSE; /* current process is a monitor */
+# endif
+
+char Buf[BUFSIZ]; /* general scribbling buffer */
+char Maze[HEIGHT][WIDTH2]; /* the maze */
+char Orig_maze[HEIGHT][WIDTH2]; /* the original maze */
+
+long Fds_mask; /* mask for the file descriptors */
+int Have_inp; /* which file descriptors have input */
+int Nplayer = 0; /* number of players */
+int Num_fds; /* number of maximum file descriptor */
+int Socket; /* main socket */
+long Sock_mask; /* select mask for main socket */
+int Status; /* stat socket */
+long Stat_mask; /* select mask for stat socket */
+int See_over[NASCII]; /* lookup table for determining whether
+ * character represents "transparent"
+ * item */
+
+BULLET *Bullets = NULL; /* linked list of bullets */
+
+EXPL *Expl[EXPLEN]; /* explosion lists */
+EXPL *Last_expl; /* last explosion on Expl[0] */
+
+PLAYER Player[MAXPL]; /* all the players */
+PLAYER *End_player = Player; /* last active player slot */
+# ifdef BOOTS
+PLAYER Boot[NBOOTS]; /* all the boots */
+# endif
+IDENT *Scores; /* score cache */
+# ifdef MONITOR
+PLAYER Monitor[MAXMON]; /* all the monitors */
+PLAYER *End_monitor = Monitor; /* last active monitor slot */
+# endif
+
+# ifdef VOLCANO
+int volcano = 0; /* Explosion size */
+# endif
+
+int shot_req[MAXBOMB] = {
+ BULREQ, GRENREQ, SATREQ,
+ BOMB7REQ, BOMB9REQ, BOMB11REQ,
+ BOMB13REQ, BOMB15REQ, BOMB17REQ,
+ BOMB19REQ, BOMB21REQ,
+ };
+int shot_type[MAXBOMB] = {
+ SHOT, GRENADE, SATCHEL,
+ BOMB, BOMB, BOMB,
+ BOMB, BOMB, BOMB,
+ BOMB, BOMB,
+ };
+
+int slime_req[MAXSLIME] = {
+ SLIMEREQ, SSLIMEREQ, SLIME2REQ, SLIME3REQ,
+ };
diff --git a/hunt/huntd/faketalk.c b/hunt/huntd/faketalk.c
new file mode 100644
index 00000000..2a50c3dd
--- /dev/null
+++ b/hunt/huntd/faketalk.c
@@ -0,0 +1,217 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ *
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#include "bsd.h"
+
+#if defined(TALK_43) || defined(TALK_42)
+
+# include <stdio.h>
+# include <string.h>
+# include <netdb.h>
+# include "talk_ctl.h"
+# include <ctype.h>
+# include <signal.h>
+# include <sys/time.h>
+extern int errno;
+
+# define TRUE 1
+# define FALSE 0
+
+/* defines for fake talk message to announce start of game */
+# ifdef TALK_43
+# define MASQUERADE "\"Hunt Game\""
+# else
+# define MASQUERADE "HuntGame"
+# endif
+# define RENDEZVOUS "hunt-players"
+# define ARGV0 "HUNT-ANNOUNCE"
+
+extern char *my_machine_name;
+extern char *First_arg, *Last_arg;
+
+/*
+ * exorcise - disspell zombies
+ */
+
+SIGNAL_TYPE
+exorcise()
+{
+ (void) wait(0);
+}
+
+/*
+ * query the local SMTP daemon to expand the RENDEZVOUS mailing list
+ * and fake a talk request to each address thus found.
+ */
+
+faketalk()
+{
+ struct servent *sp;
+ char buf[BUFSIZ];
+ FILE *f;
+ int service; /* socket of service */
+ struct sockaddr_in des; /* address of destination */
+ char *a, *b;
+ extern char **environ;
+
+ (void) signal(SIGCHLD, exorcise);
+
+ if (fork() != 0)
+ return;
+
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * change argv so that a ps shows ARGV0
+ */
+ *environ = NULL;
+ for (a = First_arg, b = ARGV0; a < Last_arg; a++) {
+ if (*b)
+ *a = *b++;
+ else
+ *a = ' ';
+ }
+
+ /*
+ * initialize "talk"
+ */
+ get_local_name(MASQUERADE);
+ open_ctl();
+
+ /*
+ * start fetching addresses
+ */
+
+ if ((sp = getservbyname("smtp", (char *) NULL)) == NULL) {
+# ifdef LOG
+ syslog(LOG_ERR, "faketalk: smtp protocol not supported\n");
+# else
+ fprintf(stderr, "faketalk: stmp protocol not supported\n");
+# endif
+ _exit(1);
+ }
+
+ memset(&des, 0, sizeof (des));
+ des.sin_family = AF_INET;
+ des.sin_addr = my_machine_addr;
+ des.sin_port = sp->s_port;
+
+ if ((service = socket(des.sin_family, SOCK_STREAM, 0)) < 0) {
+# ifdef LOG
+ syslog(LOG_ERR, "falktalk: socket");
+# else
+ perror("falktalk: socket");
+# endif
+ _exit(-1);
+ }
+
+ if (connect(service, (struct sockaddr *) &des, sizeof(des)) != 0) {
+# ifdef LOG
+ syslog(LOG_ERR, "faketalk: connect");
+# else
+ perror("faketalk: connect");
+# endif
+ _exit(-1);
+ }
+ if ((f = fdopen(service, "r")) == NULL) {
+# ifdef LOG
+ syslog(LOG_ERR, "fdopen failed\n");
+# else
+ fprintf(stderr, "fdopen failed\n");
+# endif
+ _exit(-2);
+ }
+
+ (void) fgets(buf, BUFSIZ, f);
+ (void) sprintf(buf, "HELO HuntGame@%s\r\n", my_machine_name);
+ (void) write(service, buf, strlen(buf));
+ (void) fgets(buf, BUFSIZ, f);
+ (void) sprintf(buf, "EXPN %s@%s\r\n", RENDEZVOUS, my_machine_name);
+ (void) write(service, buf, strlen(buf));
+ while (fgets(buf, BUFSIZ, f) != NULL) {
+ char *s, *t;
+
+ if (buf[0] != '2' || buf[1] != '5' || buf[2] != '0')
+ break;
+ if ((s = strchr(buf + 4, '<')) == NULL)
+ s = buf + 4, t = buf + strlen(buf) - 1;
+ else {
+ s += 1;
+ if ((t = strrchr(s, '>')) == NULL)
+ t = s + strlen(s) - 1;
+ else
+ t -= 1;
+ }
+ while (isspace(*s))
+ s += 1;
+ if (*s == '\\')
+ s += 1;
+ while (isspace(*t))
+ t -= 1;
+ *(t + 1) = '\0';
+ do_announce(s); /* construct and send talk request */
+ if (buf[3] == ' ')
+ break;
+ }
+ (void) shutdown(service, 2);
+ (void) close(service);
+ _exit(0);
+}
+
+/*
+ * The msg.id's for the invitations on the local and remote machines.
+ * These are used to delete the invitations.
+ */
+
+do_announce(s)
+ char *s;
+{
+ CTL_RESPONSE response;
+ extern struct sockaddr_in ctl_addr;
+
+ get_remote_name(s); /* setup his_machine_addr, msg.r_name */
+
+# ifdef TALK_43
+# if BSD_RELEASE >= 44
+ msg.ctl_addr = *(struct osockaddr *) &ctl_addr;
+# else
+ msg.ctl_addr = *(struct sockaddr *) &ctl_addr;
+# endif
+ msg.ctl_addr.sa_family = htons(msg.ctl_addr.sa_family);
+# else
+ msg.ctl_addr = ctl_addr;
+ msg.ctl_addr.sin_family = htons(msg.ctl_addr.sin_family);
+# endif
+ msg.id_num = (int) htonl((u_long) -1); /* an impossible id_num */
+ ctl_transact(his_machine_addr, msg, ANNOUNCE, &response);
+ if (response.answer != SUCCESS)
+ return;
+
+ /*
+ * Have the daemons delete the invitations now that we
+ * have announced.
+ */
+
+ /* we don't care if cleanup doesn't make it. */
+ msg.type = DELETE;
+ msg.id_num = (int) htonl(response.id_num);
+ daemon_addr.sin_addr = his_machine_addr;
+ if (sendto(ctl_sockt, (char *) &msg, sizeof (msg), 0,
+ (struct sockaddr *) &daemon_addr, sizeof(daemon_addr))
+ != sizeof(msg))
+ p_error("send delete remote");
+}
+#else
+faketalk()
+{
+ return;
+}
+#endif
diff --git a/hunt/huntd/get_names.c b/hunt/huntd/get_names.c
new file mode 100644
index 00000000..e8a101a0
--- /dev/null
+++ b/hunt/huntd/get_names.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#include "bsd.h"
+
+#if defined(TALK_43) || defined(TALK_42)
+
+# include <stdio.h>
+# include <string.h>
+# include "talk_ctl.h"
+# include <sys/param.h>
+# include <netdb.h>
+
+extern CTL_MSG msg;
+
+struct hostent *gethostbyname();
+struct servent *getservbyname();
+
+static char hostname[MAXHOSTNAMELEN];
+char *my_machine_name;
+
+/*
+ * Determine the local user and machine
+ */
+get_local_name(my_name)
+ char *my_name;
+{
+ struct hostent *hp;
+ struct servent *sp;
+
+ /* Load these useful values into the standard message header */
+ msg.id_num = 0;
+ (void) strncpy(msg.l_name, my_name, NAME_SIZE);
+ msg.l_name[NAME_SIZE - 1] = '\0';
+ msg.r_tty[0] = '\0';
+ msg.pid = getpid();
+# ifdef TALK_43
+ msg.vers = TALK_VERSION;
+ msg.addr.sa_family = htons(AF_INET);
+ msg.ctl_addr.sa_family = htons(AF_INET);
+# else
+ msg.addr.sin_family = htons(AF_INET);
+ msg.ctl_addr.sin_family = htons(AF_INET);
+# endif
+
+ (void) gethostname(hostname, sizeof (hostname));
+ my_machine_name = hostname;
+ /* look up the address of the local host */
+ hp = gethostbyname(my_machine_name);
+ if (hp == (struct hostent *) 0) {
+ printf("This machine doesn't exist. Boy, am I confused!\n");
+ exit(-1);
+ }
+ memcpy(&my_machine_addr, hp->h_addr, hp->h_length);
+ /* find the daemon portal */
+# ifdef TALK_43
+ sp = getservbyname("ntalk", "udp");
+# else
+ sp = getservbyname("talk", "udp");
+# endif
+ if (sp == 0) {
+# ifdef LOG
+ syslog(LOG_ERR, "This machine doesn't support talk");
+# else
+ perror("This machine doesn't support talk");
+# endif
+ exit(-1);
+ }
+ daemon_port = sp->s_port;
+}
+
+/*
+ * Determine the remote user and machine
+ */
+get_remote_name(his_address)
+ char *his_address;
+{
+ char *his_name;
+ char *his_machine_name;
+ char *ptr;
+ struct hostent *hp;
+
+
+ /* check for, and strip out, the machine name of the target */
+ for (ptr = his_address; *ptr != '\0' && *ptr != '@' && *ptr != ':'
+ && *ptr != '!' && *ptr != '.'; ptr++)
+ continue;
+ if (*ptr == '\0') {
+ /* this is a local to local talk */
+ his_name = his_address;
+ his_machine_name = my_machine_name;
+ } else {
+ if (*ptr == '@') {
+ /* user@host */
+ his_name = his_address;
+ his_machine_name = ptr + 1;
+ } else {
+ /* host.user or host!user or host:user */
+ his_name = ptr + 1;
+ his_machine_name = his_address;
+ }
+ *ptr = '\0';
+ }
+ /* Load these useful values into the standard message header */
+ (void) strncpy(msg.r_name, his_name, NAME_SIZE);
+ msg.r_name[NAME_SIZE - 1] = '\0';
+
+ /* if he is on the same machine, then simply copy */
+ if (bcmp((char *) &his_machine_name, (char *) &my_machine_name,
+ sizeof(his_machine_name)) == 0)
+ memcpy(&his_machine_addr, &my_machine_addr,
+ sizeof(his_machine_name));
+ else {
+ /* look up the address of the recipient's machine */
+ hp = gethostbyname(his_machine_name);
+ if (hp == (struct hostent *) 0)
+ return 0; /* unknown host */
+ memcpy(&his_machine_addr, hp->h_addr, hp->h_length);
+ }
+ return 1;
+}
+#endif
diff --git a/hunt/huntd/hunt.h b/hunt/huntd/hunt.h
new file mode 100644
index 00000000..3225f44f
--- /dev/null
+++ b/hunt/huntd/hunt.h
@@ -0,0 +1,400 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "bsd.h"
+
+# include <stdio.h>
+# include <string.h>
+# ifdef LOG
+# include <syslog.h>
+# endif
+# if !defined(TERMINFO) && BSD_RELEASE < 44
+# include <sgtty.h>
+# else
+# include <sys/ioctl.h>
+# endif
+# include <sys/types.h>
+# include <sys/uio.h>
+# include <sys/socket.h>
+# ifdef INTERNET
+# include <netinet/in.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+# ifdef BROADCAST
+# include <net/if.h>
+# endif
+# else
+# include <sys/un.h>
+# endif
+
+# ifdef INTERNET
+# define SOCK_FAMILY AF_INET
+# else
+# define SOCK_FAMILY AF_UNIX
+# define AF_UNIX_HACK /* 4.2 hack; leaves files around */
+# endif
+
+/*
+ * Preprocessor define dependencies
+ */
+# if defined(VOLCANO) && !defined(OOZE)
+# define OOZE
+# endif
+# if defined(BOOTS) && !defined(FLY)
+# define FLY
+# endif
+# if !defined(REFLECT) && !defined(RANDOM)
+# define RANDOM
+# endif
+# ifdef TERMINFO
+/* mvcur() in terminfo needs the curses library to be initialized to not
+ * coredump, so give up and use it. */
+# define USE_CURSES
+# endif
+
+/* decrement version number for each change in startup protocol */
+# define HUNT_VERSION -1
+
+# define ADDCH ('a' | 0200)
+# define MOVE ('m' | 0200)
+# define REFRESH ('r' | 0200)
+# define CLRTOEOL ('c' | 0200)
+# define ENDWIN ('e' | 0200)
+# define CLEAR ('C' | 0200)
+# define REDRAW ('R' | 0200)
+# define LAST_PLAYER ('l' | 0200)
+# define BELL ('b' | 0200)
+# define READY ('g' | 0200)
+
+/*
+ * Choose MAXPL and MAXMON carefully. The screen is assumed to be
+ * 23 lines high and will only tolerate (MAXPL == 17 && MAXMON == 0)
+ * or (MAXPL + MAXMON <= 16).
+ */
+# ifdef MONITOR
+# define MAXPL 15
+# define MAXMON 1
+# else
+# define MAXPL 17
+# endif
+# define SHORTLEN 2 /* sizeof (network short) */
+# define LONGLEN 4 /* sizeof (network long) */
+# define NAMELEN 20
+# define MSGLEN SCREEN_WIDTH
+# define DECAY 50.0
+
+# define NASCII 128
+
+# define WIDTH 51
+# define WIDTH2 64 /* Next power of 2 >= WIDTH (for fast access) */
+# define HEIGHT 23
+# define UBOUND 1
+# define DBOUND (HEIGHT - 1)
+# define LBOUND 1
+# define RBOUND (WIDTH - 1)
+
+# define SCREEN_HEIGHT 24
+# define SCREEN_WIDTH 80
+# define SCREEN_WIDTH2 128 /* Next power of 2 >= SCREEN_WIDTH */
+
+# define STAT_LABEL_COL 60
+# define STAT_VALUE_COL 74
+# define STAT_NAME_COL 61
+# define STAT_SCAN_COL (STAT_NAME_COL + 5)
+# define STAT_AMMO_ROW 0
+# define STAT_GUN_ROW 1
+# define STAT_DAM_ROW 2
+# define STAT_KILL_ROW 3
+# define STAT_PLAY_ROW 5
+# ifdef MONITOR
+# define STAT_MON_ROW (STAT_PLAY_ROW + MAXPL + 1)
+# endif
+# define STAT_NAME_LEN 18
+
+# define DOOR '#'
+# define WALL1 '-'
+# define WALL2 '|'
+# define WALL3 '+'
+# ifdef REFLECT
+# define WALL4 '/'
+# define WALL5 '\\'
+# endif
+# define KNIFE 'K'
+# define SHOT ':'
+# define GRENADE 'o'
+# define SATCHEL 'O'
+# define BOMB '@'
+# define MINE ';'
+# define GMINE 'g'
+# ifdef OOZE
+# define SLIME '$'
+# endif
+# ifdef VOLCANO
+# define LAVA '~'
+# endif
+# ifdef DRONE
+# define DSHOT '?'
+# endif
+# ifdef FLY
+# define FALL 'F'
+# endif
+# ifdef BOOTS
+# define NBOOTS 2
+# define BOOT 'b'
+# define BOOT_PAIR 'B'
+# endif
+# define SPACE ' '
+
+# define ABOVE 'i'
+# define BELOW '!'
+# define RIGHT '}'
+# define LEFTS '{'
+# ifdef FLY
+# define FLYER '&'
+# define isplayer(c) (c == LEFTS || c == RIGHT ||\
+ c == ABOVE || c == BELOW || c == FLYER)
+# else
+# define isplayer(c) (c == LEFTS || c == RIGHT ||\
+ c == ABOVE || c == BELOW)
+# endif
+
+# define NORTH 01
+# define SOUTH 02
+# define EAST 010
+# define WEST 020
+
+# ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+# endif
+# undef CTRL
+# define CTRL(x) ((x) & 037)
+
+# define BULSPD 5 /* bullets movement speed */
+# define ISHOTS 15
+# define NSHOTS 5
+# define MAXNCSHOT 2
+# define MAXDAM 10
+# define MINDAM 5
+# define STABDAM 2
+
+# define BULREQ 1
+# define GRENREQ 9
+# define SATREQ 25
+# define BOMB7REQ 49
+# define BOMB9REQ 81
+# define BOMB11REQ 121
+# define BOMB13REQ 169
+# define BOMB15REQ 225
+# define BOMB17REQ 289
+# define BOMB19REQ 361
+# define BOMB21REQ 441
+# define MAXBOMB 11
+# ifdef DRONE
+# define MINDSHOT 2 /* At least a satchel bomb */
+# endif
+extern int shot_req[];
+extern int shot_type[];
+# ifdef OOZE
+# define SLIME_FACTOR 3
+# define SLIMEREQ 5
+# define SSLIMEREQ 10
+# define SLIME2REQ 15
+# define SLIME3REQ 20
+# define MAXSLIME 4
+# define SLIMESPEED 5
+extern int slime_req[];
+# endif
+# ifdef VOLCANO
+# define LAVASPEED 1
+# endif
+
+# define CLOAKLEN 20
+# define SCANLEN (Nplayer * 20)
+# define EXPLEN 4
+
+# define Q_QUIT 0
+# define Q_CLOAK 1
+# define Q_FLY 2
+# define Q_SCAN 3
+# define Q_MESSAGE 4
+
+# define C_PLAYER 0
+# define C_MONITOR 1
+# define C_MESSAGE 2
+# define C_SCORES 3
+
+# ifdef MONITOR
+# define C_TESTMSG() (Query_driver ? C_MESSAGE :\
+ (Show_scores ? C_SCORES :\
+ (Am_monitor ? C_MONITOR :\
+ C_PLAYER)))
+# else
+# define C_TESTMSG() (Show_scores ? C_SCORES :\
+ (Query_driver ? C_MESSAGE :\
+ C_PLAYER))
+# endif
+
+# ifdef FLY
+# define _scan_char(pp) (((pp)->p_scan < 0) ? ' ' : '*')
+# define _cloak_char(pp) (((pp)->p_cloak < 0) ? _scan_char(pp) : '+')
+# define stat_char(pp) (((pp)->p_flying < 0) ? _cloak_char(pp) : FLYER)
+# else
+# define _scan_char(pp) (((pp)->p_scan < 0) ? ' ' : '*')
+# define stat_char(pp) (((pp)->p_cloak < 0) ? _scan_char(pp) : '+')
+# endif
+
+typedef int FLAG;
+typedef struct bullet_def BULLET;
+typedef struct expl_def EXPL;
+typedef struct player_def PLAYER;
+typedef struct ident_def IDENT;
+typedef struct regen_def REGEN;
+# ifdef INTERNET
+typedef struct sockaddr_in SOCKET;
+# else
+typedef struct sockaddr_un SOCKET;
+# endif
+typedef struct sgttyb TTYB;
+
+struct ident_def {
+ char i_name[NAMELEN];
+ char i_team;
+ long i_machine;
+ long i_uid;
+ float i_kills;
+ int i_entries;
+ float i_score;
+ int i_absorbed;
+ int i_faced;
+ int i_shot;
+ int i_robbed;
+ int i_slime;
+ int i_missed;
+ int i_ducked;
+ int i_gkills, i_bkills, i_deaths, i_stillb, i_saved;
+ IDENT *i_next;
+};
+
+struct player_def {
+ IDENT *p_ident;
+ char p_over;
+ int p_face;
+ int p_undershot;
+# ifdef FLY
+ int p_flying;
+ int p_flyx, p_flyy;
+# endif
+# ifdef BOOTS
+ int p_nboots;
+# endif
+ FILE *p_output;
+ int p_fd;
+ int p_mask;
+ int p_damage;
+ int p_damcap;
+ int p_ammo;
+ int p_ncshot;
+ int p_scan;
+ int p_cloak;
+ int p_x, p_y;
+ int p_ncount;
+ int p_nexec;
+ long p_nchar;
+ char p_death[MSGLEN];
+ char p_maze[HEIGHT][WIDTH2];
+ int p_curx, p_cury;
+ int p_lastx, p_lasty;
+ char p_cbuf[BUFSIZ];
+};
+
+struct bullet_def {
+ int b_x, b_y;
+ int b_face;
+ int b_charge;
+ char b_type;
+ char b_size;
+ char b_over;
+ PLAYER *b_owner;
+ IDENT *b_score;
+ FLAG b_expl;
+ BULLET *b_next;
+};
+
+struct expl_def {
+ int e_x, e_y;
+ char e_char;
+ EXPL *e_next;
+};
+
+struct regen_def {
+ int r_x, r_y;
+ REGEN *r_next;
+};
+
+/*
+ * external variables
+ */
+
+extern FLAG Last_player;
+
+extern char Buf[BUFSIZ], Maze[HEIGHT][WIDTH2], Orig_maze[HEIGHT][WIDTH2];
+
+extern char *Sock_name, *Driver;
+
+extern int errno, Have_inp, Nplayer, Num_fds, Socket, Status;
+extern long Fds_mask, Sock_mask, Stat_mask;
+
+# ifdef INTERNET
+extern u_short Test_port;
+# else
+extern char *Sock_name;
+# endif
+
+# ifdef VOLCANO
+extern int volcano;
+# endif
+
+extern int See_over[NASCII];
+
+extern BULLET *Bullets;
+
+extern EXPL *Expl[EXPLEN];
+extern EXPL *Last_expl;
+
+extern IDENT *Scores;
+
+extern PLAYER Player[MAXPL], *End_player;
+# ifdef BOOTS
+extern PLAYER Boot[NBOOTS];
+# endif
+
+# ifdef MONITOR
+extern FLAG Am_monitor;
+extern PLAYER Monitor[MAXMON], *End_monitor;
+# endif
+
+# ifdef INTERNET
+extern char *Send_message;
+# endif
+
+extern char map_key[256];
+extern FLAG no_beep;
+
+/*
+ * function types
+ */
+
+extern char *getenv();
+extern void *malloc(), *realloc();
+
+extern IDENT *get_ident();
+
+extern int moveshots();
+
+extern BULLET *is_bullet(), *create_shot();
+
+extern PLAYER *play_at();
diff --git a/hunt/huntd/huntd.6 b/hunt/huntd/huntd.6
new file mode 100644
index 00000000..d1705a10
--- /dev/null
+++ b/hunt/huntd/huntd.6
@@ -0,0 +1,98 @@
+.\" Hunt
+.\" Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+.\" San Francisco, California
+.\"
+.\" Copyright (c) 1985 Regents of the University of California.
+.\" All rights reserved. The Berkeley software License Agreement
+.\" specifies the terms and conditions for redistribution.
+.\"
+.TH HUNTD 6 "21 August 1986"
+.UC 4
+.SH NAME
+huntd \- hunt daemon, back-end for hunt game
+.SH SYNOPSIS
+\fB/usr/games/lib/huntd\fP [ \fB\-s\fP ] [ \fB\-p\fP port ]
+.SH DESCRIPTION
+.PP
+.I huntd
+controls the multi-player
+.IR hunt (6)
+game.
+When it starts up, it tries to notify all members of the
+.I hunt-players
+mailing list (see
+.IR sendmail (8))
+by faking a
+.IR talk (1)
+request from user ``Hunt Game''.
+.PP
+The
+.B \-s
+option is for running
+.I huntd
+forever (server mode).
+This is similar to running it under the control of
+.I inetd
+(see below),
+but it consumes a process table entry when no one is playing.
+.PP
+The
+.B \-p
+option changes the udp port number used to rendezvous with the player
+process and thus allows for private games of hunt.
+This option turns off the notification of players on the
+.I hunt-players
+mailing list.
+.SH INETD
+.PP
+To run
+.I huntd
+from
+.IR inetd ,
+you'll need to put the
+.I hunt
+service in
+.BR /etc/services :
+.IP
+hunt 26740/udp # multi-player/multi-host mazewars
+.LP
+and add a line in
+.BR /etc/inetd.conf :
+.IP
+hunt dgram udp wait nobody /usr/games/lib/huntd HUNT
+.LP
+except for Suns which use
+.BR /etc/servers :
+.IP
+hunt udp /usr/games/lib/huntd
+.LP
+Do not use any of the command line options \(em if you want
+.I inetd
+to start up
+.I huntd
+on a private port, change the port listed in
+.BR /etc/services .
+.SH "NETWORK RENDEZVOUS"
+When
+.IR hunt (6)
+starts up, it broadcasts on the local area net
+(using the broadcast address for each interface) to find a
+.I hunt
+game in progress.
+If a
+.I huntd
+hears the request, it sends back the port number for the
+.I hunt
+process to connect to.
+Otherwise, the
+.I hunt
+process starts up a
+.I huntd
+on the local machine and trys to rendezvous with it.
+.SH "SEE ALSO"
+hunt(6), talk(1), sendmail(8)
+.SH AUTHORS
+Conrad Huang, Ken Arnold, and Greg Couch;
+.br
+University of California, San Francisco, Computer Graphics Lab
+.\"SH BUGS
diff --git a/hunt/huntd/makemaze.c b/hunt/huntd/makemaze.c
new file mode 100644
index 00000000..d0e88960
--- /dev/null
+++ b/hunt/huntd/makemaze.c
@@ -0,0 +1,191 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+
+# define ISCLEAR(y,x) (Maze[y][x] == SPACE)
+# define ODD(n) ((n) & 01)
+
+makemaze()
+{
+ register char *sp;
+ register int y, x;
+
+ /*
+ * fill maze with walls
+ */
+ sp = &Maze[0][0];
+ while (sp < &Maze[HEIGHT - 1][WIDTH])
+ *sp++ = DOOR;
+
+ x = rand_num(WIDTH / 2) * 2 + 1;
+ y = rand_num(HEIGHT / 2) * 2 + 1;
+ dig_maze(x, y);
+ remap();
+}
+
+# define NPERM 24
+# define NDIR 4
+
+int dirs[NPERM][NDIR] = {
+ {0,1,2,3}, {3,0,1,2}, {0,2,3,1}, {0,3,2,1},
+ {1,0,2,3}, {2,3,0,1}, {0,2,1,3}, {2,3,1,0},
+ {1,0,3,2}, {1,2,0,3}, {3,1,2,0}, {2,0,3,1},
+ {1,3,0,2}, {0,3,1,2}, {1,3,2,0}, {2,0,1,3},
+ {0,1,3,2}, {3,1,0,2}, {2,1,0,3}, {1,2,3,0},
+ {2,1,3,0}, {3,0,2,1}, {3,2,0,1}, {3,2,1,0}
+ };
+
+int incr[NDIR][2] = {
+ {0, 1}, {1, 0}, {0, -1}, {-1, 0}
+ };
+
+dig(y, x)
+int y, x;
+{
+ register int *dp;
+ register int *ip;
+ register int ny, nx;
+ register int *endp;
+
+ Maze[y][x] = SPACE; /* Clear this spot */
+ dp = dirs[rand_num(NPERM)];
+ endp = &dp[NDIR];
+ while (dp < endp) {
+ ip = &incr[*dp++][0];
+ ny = y + *ip++;
+ nx = x + *ip;
+ if (candig(ny, nx))
+ dig(ny, nx);
+ }
+}
+
+/*
+ * candig:
+ * Is it legal to clear this spot?
+ */
+candig(y, x)
+register int y, x;
+{
+ register int i;
+
+ if (ODD(x) && ODD(y))
+ return FALSE; /* can't touch ODD spots */
+
+ if (y < UBOUND || y >= DBOUND)
+ return FALSE; /* Beyond vertical bounds, NO */
+ if (x < LBOUND || x >= RBOUND)
+ return FALSE; /* Beyond horizontal bounds, NO */
+
+ if (ISCLEAR(y, x))
+ return FALSE; /* Already clear, NO */
+
+ i = ISCLEAR(y, x + 1);
+ i += ISCLEAR(y, x - 1);
+ if (i > 1)
+ return FALSE; /* Introduces cycle, NO */
+ i += ISCLEAR(y + 1, x);
+ if (i > 1)
+ return FALSE; /* Introduces cycle, NO */
+ i += ISCLEAR(y - 1, x);
+ if (i > 1)
+ return FALSE; /* Introduces cycle, NO */
+
+ return TRUE; /* OK */
+}
+
+dig_maze(x, y)
+int x, y;
+{
+ register int tx, ty;
+ register int i, j;
+ int order[4];
+#define MNORTH 0x1
+#define MSOUTH 0x2
+#define MEAST 0x4
+#define MWEST 0x8
+
+ Maze[y][x] = SPACE;
+ order[0] = MNORTH;
+ for (i = 1; i < 4; i++) {
+ j = rand_num(i + 1);
+ order[i] = order[j];
+ order[j] = 0x1 << i;
+ }
+ for (i = 0; i < 4; i++) {
+ switch (order[i]) {
+ case MNORTH:
+ tx = x;
+ ty = y - 2;
+ break;
+ case MSOUTH:
+ tx = x;
+ ty = y + 2;
+ break;
+ case MEAST:
+ tx = x + 2;
+ ty = y;
+ break;
+ case MWEST:
+ tx = x - 2;
+ ty = y;
+ break;
+ }
+ if (tx < 0 || ty < 0 || tx >= WIDTH || ty >= HEIGHT)
+ continue;
+ if (Maze[ty][tx] == SPACE)
+ continue;
+ Maze[(y + ty) / 2][(x + tx) / 2] = SPACE;
+ dig_maze(tx, ty);
+ }
+}
+
+remap()
+{
+ register int y, x;
+ register char *sp;
+ register int stat;
+
+ for (y = 0; y < HEIGHT; y++)
+ for (x = 0; x < WIDTH; x++) {
+ sp = &Maze[y][x];
+ if (*sp == SPACE)
+ continue;
+ stat = 0;
+ if (y - 1 >= 0 && Maze[y - 1][x] != SPACE)
+ stat |= NORTH;
+ if (y + 1 < HEIGHT && Maze[y + 1][x] != SPACE)
+ stat |= SOUTH;
+ if (x + 1 < WIDTH && Maze[y][x + 1] != SPACE)
+ stat |= EAST;
+ if (x - 1 >= 0 && Maze[y][x - 1] != SPACE)
+ stat |= WEST;
+ switch (stat) {
+ case WEST | EAST:
+ case EAST:
+ case WEST:
+ *sp = WALL1;
+ break;
+ case NORTH | SOUTH:
+ case NORTH:
+ case SOUTH:
+ *sp = WALL2;
+ break;
+ case 0:
+# ifdef RANDOM
+ *sp = DOOR;
+# endif
+# ifdef REFLECT
+ *sp = rand_num(2) ? WALL4 : WALL5;
+# endif
+ break;
+ default:
+ *sp = WALL3;
+ break;
+ }
+ }
+ memcpy(Orig_maze, Maze, sizeof Maze);
+}
diff --git a/hunt/huntd/pathname.c b/hunt/huntd/pathname.c
new file mode 100644
index 00000000..90b4d380
--- /dev/null
+++ b/hunt/huntd/pathname.c
@@ -0,0 +1,35 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+/*
+ * There is no particular significance to the numbers assigned
+ * to Test_port. They're just random numbers greater than the
+ * range reserved for privileged sockets.
+ */
+
+# include <sys/types.h>
+
+# ifdef DEBUG
+
+char *Driver = "/home/socr/a/conrad/games/src/hunt/huntd.dbg";
+# ifdef INTERNET
+u_short Test_port = ('h' << 8) | 't';
+# else
+char *Sock_name = "/tmp/hunt";
+char *Stat_name = "/tmp/hunt.stats";
+# endif
+
+# else
+
+char *Driver = HUNTD;
+# ifdef INTERNET
+u_short Test_port = ('h' << 8) | 't';
+# else
+char *Sock_name = "/tmp/hunt";
+char *Stat_name = "/tmp/hunt.stats";
+# endif
+
+# endif
diff --git a/hunt/huntd/shots.c b/hunt/huntd/shots.c
new file mode 100644
index 00000000..3aa7284c
--- /dev/null
+++ b/hunt/huntd/shots.c
@@ -0,0 +1,1096 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+# include <signal.h>
+
+# define PLUS_DELTA(x, max) if (x < max) x++; else x--
+# define MINUS_DELTA(x, min) if (x > min) x--; else x++
+
+/*
+ * moveshots:
+ * Move the shots already in the air, taking explosions into account
+ */
+moveshots()
+{
+ register BULLET *bp, *next;
+ register PLAYER *pp;
+ register int x, y;
+ register BULLET *blist;
+
+ rollexpl();
+ if (Bullets == NULL)
+ goto ret;
+
+ /*
+ * First we move through the bullet list BULSPD times, looking
+ * for things we may have run into. If we do run into
+ * something, we set up the explosion and disappear, checking
+ * for damage to any player who got in the way.
+ */
+
+ blist = Bullets;
+ Bullets = NULL;
+ for (bp = blist; bp != NULL; bp = next) {
+ next = bp->b_next;
+ x = bp->b_x;
+ y = bp->b_y;
+ Maze[y][x] = bp->b_over;
+ for (pp = Player; pp < End_player; pp++)
+ check(pp, y, x);
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, y, x);
+# endif
+
+ switch (bp->b_type) {
+ case SHOT:
+ case GRENADE:
+ case SATCHEL:
+ case BOMB:
+ if (move_normal_shot(bp)) {
+ bp->b_next = Bullets;
+ Bullets = bp;
+ }
+ break;
+# ifdef OOZE
+ case SLIME:
+ if (bp->b_expl || move_normal_shot(bp)) {
+ bp->b_next = Bullets;
+ Bullets = bp;
+ }
+ break;
+# endif
+# ifdef DRONE
+ case DSHOT:
+ if (move_drone(bp)) {
+ bp->b_next = Bullets;
+ Bullets = bp;
+ }
+ break;
+# endif
+ default:
+ bp->b_next = Bullets;
+ Bullets = bp;
+ break;
+ }
+ }
+
+ blist = Bullets;
+ Bullets = NULL;
+ for (bp = blist; bp != NULL; bp = next) {
+ next = bp->b_next;
+ if (!bp->b_expl) {
+ save_bullet(bp);
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, bp->b_y, bp->b_x);
+# endif
+# ifdef DRONE
+ if (bp->b_type == DSHOT)
+ for (pp = Player; pp < End_player; pp++)
+ if (pp->p_scan >= 0)
+ check(pp, bp->b_y, bp->b_x);
+# endif
+ continue;
+ }
+
+ chkshot(bp, next);
+ free((char *) bp);
+ }
+
+ for (pp = Player; pp < End_player; pp++)
+ Maze[pp->p_y][pp->p_x] = pp->p_face;
+
+ret:
+# ifdef BOOTS
+ for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
+ if (pp->p_flying >= 0)
+ move_flyer(pp);
+# endif
+ for (pp = Player; pp < End_player; pp++) {
+# ifdef FLY
+ if (pp->p_flying >= 0)
+ move_flyer(pp);
+# endif
+ sendcom(pp, REFRESH); /* Flush out the explosions */
+ look(pp);
+ sendcom(pp, REFRESH);
+ }
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ sendcom(pp, REFRESH);
+# endif
+
+ return;
+}
+
+/*
+ * move_normal_shot:
+ * Move a normal shot along its trajectory
+ */
+move_normal_shot(bp)
+register BULLET *bp;
+{
+ register int i, x, y;
+ register PLAYER *pp;
+
+ for (i = 0; i < BULSPD; i++) {
+ if (bp->b_expl)
+ break;
+
+ x = bp->b_x;
+ y = bp->b_y;
+
+ switch (bp->b_face) {
+ case LEFTS:
+ x--;
+ break;
+ case RIGHT:
+ x++;
+ break;
+ case ABOVE:
+ y--;
+ break;
+ case BELOW:
+ y++;
+ break;
+ }
+
+ switch (Maze[y][x]) {
+ case SHOT:
+ if (rand_num(100) < 5) {
+ zapshot(Bullets, bp);
+ zapshot(bp->b_next, bp);
+ }
+ break;
+ case GRENADE:
+ if (rand_num(100) < 10) {
+ zapshot(Bullets, bp);
+ zapshot(bp->b_next, bp);
+ }
+ break;
+# ifdef REFLECT
+ case WALL4: /* reflecting walls */
+ switch (bp->b_face) {
+ case LEFTS:
+ bp->b_face = BELOW;
+ break;
+ case RIGHT:
+ bp->b_face = ABOVE;
+ break;
+ case ABOVE:
+ bp->b_face = RIGHT;
+ break;
+ case BELOW:
+ bp->b_face = LEFTS;
+ break;
+ }
+ Maze[y][x] = WALL5;
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, y, x);
+# endif
+ break;
+ case WALL5:
+ switch (bp->b_face) {
+ case LEFTS:
+ bp->b_face = ABOVE;
+ break;
+ case RIGHT:
+ bp->b_face = BELOW;
+ break;
+ case ABOVE:
+ bp->b_face = LEFTS;
+ break;
+ case BELOW:
+ bp->b_face = RIGHT;
+ break;
+ }
+ Maze[y][x] = WALL4;
+# ifdef MONITOR
+ for (pp = Monitor; pp < End_monitor; pp++)
+ check(pp, y, x);
+# endif
+ break;
+# endif
+# ifdef RANDOM
+ case DOOR:
+ switch (rand_num(4)) {
+ case 0:
+ bp->b_face = ABOVE;
+ break;
+ case 1:
+ bp->b_face = BELOW;
+ break;
+ case 2:
+ bp->b_face = LEFTS;
+ break;
+ case 3:
+ bp->b_face = RIGHT;
+ break;
+ }
+ break;
+# endif
+# ifdef FLY
+ case FLYER:
+ pp = play_at(y, x);
+ message(pp, "Zing!");
+ break;
+# endif
+ case LEFTS:
+ case RIGHT:
+ case BELOW:
+ case ABOVE:
+ /*
+ * give the person a chance to catch a
+ * grenade if s/he is facing it
+ */
+ pp = play_at(y, x);
+ pp->p_ident->i_shot += bp->b_charge;
+ if (opposite(bp->b_face, Maze[y][x])) {
+ if (rand_num(100) < 10) {
+ if (bp->b_owner != NULL)
+ message(bp->b_owner,
+ "Your charge was absorbed!");
+ if (bp->b_score != NULL)
+ bp->b_score->i_robbed += bp->b_charge;
+ pp->p_ammo += bp->b_charge;
+ if (pp->p_damage + bp->b_size * MINDAM
+ > pp->p_damcap)
+ pp->p_ident->i_saved++;
+ message(pp, "Absorbed charge (good shield!)");
+ pp->p_ident->i_absorbed += bp->b_charge;
+ free((char *) bp);
+ (void) sprintf(Buf, "%3d", pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+ return FALSE;
+ }
+ pp->p_ident->i_faced += bp->b_charge;
+ }
+ /*
+ * Small chance that the bullet just misses the
+ * person. If so, the bullet just goes on its
+ * merry way without exploding.
+ */
+ if (rand_num(100) < 5) {
+ pp->p_ident->i_ducked += bp->b_charge;
+ if (pp->p_damage + bp->b_size * MINDAM
+ > pp->p_damcap)
+ pp->p_ident->i_saved++;
+ if (bp->b_score != NULL)
+ bp->b_score->i_missed += bp->b_charge;
+ message(pp, "Zing!");
+ if (bp->b_owner == NULL)
+ break;
+ message(bp->b_owner,
+ ((bp->b_score->i_missed & 0x7) == 0x7) ?
+ "My! What a bad shot you are!" :
+ "Missed him");
+ break;
+ }
+ /*
+ * The shot hit that sucker! Blow it up.
+ */
+ /* FALLTHROUGH */
+# ifndef RANDOM
+ case DOOR:
+# endif
+ case WALL1:
+ case WALL2:
+ case WALL3:
+ bp->b_expl = TRUE;
+ break;
+ }
+
+ bp->b_x = x;
+ bp->b_y = y;
+ }
+ return TRUE;
+}
+
+# ifdef DRONE
+/*
+ * move_drone:
+ * Move the drone to the next square
+ */
+move_drone(bp)
+register BULLET *bp;
+{
+ register int mask, count;
+ register int n, dir;
+ register PLAYER *pp;
+
+ /*
+ * See if we can give someone a blast
+ */
+ if (isplayer(Maze[bp->b_y][bp->b_x - 1])) {
+ dir = WEST;
+ goto drone_move;
+ }
+ if (isplayer(Maze[bp->b_y - 1][bp->b_x])) {
+ dir = NORTH;
+ goto drone_move;
+ }
+ if (isplayer(Maze[bp->b_y + 1][bp->b_x])) {
+ dir = SOUTH;
+ goto drone_move;
+ }
+ if (isplayer(Maze[bp->b_y][bp->b_x + 1])) {
+ dir = EAST;
+ goto drone_move;
+ }
+
+ /*
+ * Find out what directions are clear
+ */
+ mask = count = 0;
+ if (!iswall(bp->b_y, bp->b_x - 1))
+ mask |= WEST, count++;
+ if (!iswall(bp->b_y - 1, bp->b_x))
+ mask |= NORTH, count++;
+ if (!iswall(bp->b_y + 1, bp->b_x))
+ mask |= SOUTH, count++;
+ if (!iswall(bp->b_y, bp->b_x + 1))
+ mask |= EAST, count++;
+
+ /*
+ * All blocked up, just you wait
+ */
+ if (count == 0)
+ return TRUE;
+
+ /*
+ * Only one way to go.
+ */
+ if (count == 1) {
+ dir = mask;
+ goto drone_move;
+ }
+
+ /*
+ * Get rid of the direction that we came from
+ */
+ switch (bp->b_face) {
+ case LEFTS:
+ if (mask & EAST)
+ mask &= ~EAST, count--;
+ break;
+ case RIGHT:
+ if (mask & WEST)
+ mask &= ~WEST, count--;
+ break;
+ case ABOVE:
+ if (mask & SOUTH)
+ mask &= ~SOUTH, count--;
+ break;
+ case BELOW:
+ if (mask & NORTH)
+ mask &= ~NORTH, count--;
+ break;
+ }
+
+ /*
+ * Pick one of the remaining directions
+ */
+ n = rand_num(count);
+ if (n >= 0 && mask & NORTH)
+ dir = NORTH, n--;
+ if (n >= 0 && mask & SOUTH)
+ dir = SOUTH, n--;
+ if (n >= 0 && mask & EAST)
+ dir = EAST, n--;
+ if (n >= 0 && mask & WEST)
+ dir = WEST, n--;
+
+ /*
+ * Now that we know the direction of movement,
+ * just update the position of the drone
+ */
+drone_move:
+ switch (dir) {
+ case WEST:
+ bp->b_x--;
+ bp->b_face = LEFTS;
+ break;
+ case EAST:
+ bp->b_x++;
+ bp->b_face = RIGHT;
+ break;
+ case NORTH:
+ bp->b_y--;
+ bp->b_face = ABOVE;
+ break;
+ case SOUTH:
+ bp->b_y++;
+ bp->b_face = BELOW;
+ break;
+ }
+ switch (Maze[bp->b_y][bp->b_x]) {
+ case LEFTS:
+ case RIGHT:
+ case BELOW:
+ case ABOVE:
+ /*
+ * give the person a chance to catch a
+ * drone if s/he is facing it
+ */
+ if (rand_num(100) < 1 &&
+ opposite(bp->b_face, Maze[bp->b_y][bp->b_x])) {
+ pp = play_at(bp->b_y, bp->b_x);
+ pp->p_ammo += bp->b_charge;
+ message(pp, "**** Absorbed drone ****");
+ free((char *) bp);
+ (void) sprintf(Buf, "%3d", pp->p_ammo);
+ cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
+ outstr(pp, Buf, 3);
+ return FALSE;
+ }
+ bp->b_expl = TRUE;
+ break;
+ }
+ return TRUE;
+}
+# endif
+
+/*
+ * save_bullet:
+ * Put this bullet back onto the bullet list
+ */
+save_bullet(bp)
+register BULLET *bp;
+{
+ bp->b_over = Maze[bp->b_y][bp->b_x];
+ switch (bp->b_over) {
+ case SHOT:
+ case GRENADE:
+ case SATCHEL:
+ case BOMB:
+# ifdef OOZE
+ case SLIME:
+# ifdef VOLCANO
+ case LAVA:
+# endif
+# endif
+# ifdef DRONE
+ case DSHOT:
+# endif
+ find_under(Bullets, bp);
+ break;
+ }
+
+ switch (bp->b_over) {
+ case LEFTS:
+ case RIGHT:
+ case ABOVE:
+ case BELOW:
+# ifdef FLY
+ case FLYER:
+# endif
+ mark_player(bp);
+ break;
+# ifdef BOOTS
+ case BOOT:
+ case BOOT_PAIR:
+ mark_boot(bp);
+# endif
+
+ default:
+ Maze[bp->b_y][bp->b_x] = bp->b_type;
+ break;
+ }
+
+ bp->b_next = Bullets;
+ Bullets = bp;
+}
+
+/*
+ * move_flyer:
+ * Update the position of a player in flight
+ */
+move_flyer(pp)
+register PLAYER *pp;
+{
+ register int x, y;
+
+ if (pp->p_undershot) {
+ fixshots(pp->p_y, pp->p_x, pp->p_over);
+ pp->p_undershot = FALSE;
+ }
+ Maze[pp->p_y][pp->p_x] = pp->p_over;
+ x = pp->p_x + pp->p_flyx;
+ y = pp->p_y + pp->p_flyy;
+ if (x < 1) {
+ x = 1 - x;
+ pp->p_flyx = -pp->p_flyx;
+ }
+ else if (x > WIDTH - 2) {
+ x = (WIDTH - 2) - (x - (WIDTH - 2));
+ pp->p_flyx = -pp->p_flyx;
+ }
+ if (y < 1) {
+ y = 1 - y;
+ pp->p_flyy = -pp->p_flyy;
+ }
+ else if (y > HEIGHT - 2) {
+ y = (HEIGHT - 2) - (y - (HEIGHT - 2));
+ pp->p_flyy = -pp->p_flyy;
+ }
+again:
+ switch (Maze[y][x]) {
+ default:
+ switch (rand_num(4)) {
+ case 0:
+ PLUS_DELTA(x, WIDTH - 2);
+ break;
+ case 1:
+ MINUS_DELTA(x, 1);
+ break;
+ case 2:
+ PLUS_DELTA(y, HEIGHT - 2);
+ break;
+ case 3:
+ MINUS_DELTA(y, 1);
+ break;
+ }
+ goto again;
+ case WALL1:
+ case WALL2:
+ case WALL3:
+# ifdef REFLECT
+ case WALL4:
+ case WALL5:
+# endif
+# ifdef RANDOM
+ case DOOR:
+# endif
+ if (pp->p_flying == 0)
+ pp->p_flying++;
+ break;
+ case SPACE:
+ break;
+ }
+ pp->p_y = y;
+ pp->p_x = x;
+ if (pp->p_flying-- == 0) {
+# ifdef BOOTS
+ if (pp->p_face != BOOT && pp->p_face != BOOT_PAIR) {
+# endif
+ checkdam(pp, (PLAYER *) NULL, (IDENT *) NULL,
+ rand_num(pp->p_damage / 5), FALL);
+ pp->p_face = rand_dir();
+ showstat(pp);
+# ifdef BOOTS
+ }
+ else {
+ if (Maze[y][x] == BOOT)
+ pp->p_face = BOOT_PAIR;
+ Maze[y][x] = SPACE;
+ }
+# endif
+ }
+ pp->p_over = Maze[y][x];
+ Maze[y][x] = pp->p_face;
+ showexpl(y, x, pp->p_face);
+}
+
+/*
+ * chkshot
+ * Handle explosions
+ */
+chkshot(bp, next)
+register BULLET *bp;
+BULLET *next;
+{
+ register int y, x;
+ register int dy, dx, absdy;
+ register int delta, damage;
+ register char expl;
+ register PLAYER *pp;
+
+ switch (bp->b_type) {
+ case SHOT:
+ case MINE:
+ case GRENADE:
+ case GMINE:
+ case SATCHEL:
+ case BOMB:
+ delta = bp->b_size - 1;
+ break;
+# ifdef OOZE
+ case SLIME:
+# ifdef VOLCANO
+ case LAVA:
+# endif
+ chkslime(bp, next);
+ return;
+# endif
+# ifdef DRONE
+ case DSHOT:
+ bp->b_type = SLIME;
+ chkslime(bp, next);
+ return;
+# endif
+ }
+ for (y = bp->b_y - delta; y <= bp->b_y + delta; y++) {
+ if (y < 0 || y >= HEIGHT)
+ continue;
+ dy = y - bp->b_y;
+ absdy = (dy < 0) ? -dy : dy;
+ for (x = bp->b_x - delta; x <= bp->b_x + delta; x++) {
+ if (x < 0 || x >= WIDTH)
+ continue;
+ dx = x - bp->b_x;
+ if (dx == 0)
+ expl = (dy == 0) ? '*' : '|';
+ else if (dy == 0)
+ expl = '-';
+ else if (dx == dy)
+ expl = '\\';
+ else if (dx == -dy)
+ expl = '/';
+ else
+ expl = '*';
+ showexpl(y, x, expl);
+ switch (Maze[y][x]) {
+ case LEFTS:
+ case RIGHT:
+ case ABOVE:
+ case BELOW:
+# ifdef FLY
+ case FLYER:
+# endif
+ if (dx < 0)
+ dx = -dx;
+ if (absdy > dx)
+ damage = bp->b_size - absdy;
+ else
+ damage = bp->b_size - dx;
+ pp = play_at(y, x);
+ checkdam(pp, bp->b_owner, bp->b_score,
+ damage * MINDAM, bp->b_type);
+ break;
+ case GMINE:
+ case MINE:
+ add_shot((Maze[y][x] == GMINE) ?
+ GRENADE : SHOT,
+ y, x, LEFTS,
+ (Maze[y][x] == GMINE) ?
+ GRENREQ : BULREQ,
+ (PLAYER *) NULL, TRUE, SPACE);
+ Maze[y][x] = SPACE;
+ break;
+ }
+ }
+ }
+}
+
+# ifdef OOZE
+/*
+ * chkslime:
+ * handle slime shot exploding
+ */
+chkslime(bp, next)
+register BULLET *bp;
+BULLET *next;
+{
+ register BULLET *nbp;
+
+ switch (Maze[bp->b_y][bp->b_x]) {
+ case WALL1:
+ case WALL2:
+ case WALL3:
+# ifdef REFLECT
+ case WALL4:
+ case WALL5:
+# endif
+# ifdef RANDOM
+ case DOOR:
+# endif
+ switch (bp->b_face) {
+ case LEFTS:
+ bp->b_x++;
+ break;
+ case RIGHT:
+ bp->b_x--;
+ break;
+ case ABOVE:
+ bp->b_y++;
+ break;
+ case BELOW:
+ bp->b_y--;
+ break;
+ }
+ break;
+ }
+ nbp = (BULLET *) malloc(sizeof (BULLET));
+ *nbp = *bp;
+# ifdef VOLCANO
+ move_slime(nbp, nbp->b_type == SLIME ? SLIMESPEED : LAVASPEED, next);
+# else
+ move_slime(nbp, SLIMESPEED, next);
+# endif
+}
+
+/*
+ * move_slime:
+ * move the given slime shot speed times and add it back if
+ * it hasn't fizzled yet
+ */
+move_slime(bp, speed, next)
+register BULLET *bp;
+register int speed;
+BULLET *next;
+{
+ register int i, j, dirmask, count;
+ register PLAYER *pp;
+ register BULLET *nbp;
+
+ if (speed == 0) {
+ if (bp->b_charge <= 0)
+ free((char *) bp);
+ else
+ save_bullet(bp);
+ return;
+ }
+
+# ifdef VOLCANO
+ showexpl(bp->b_y, bp->b_x, bp->b_type == LAVA ? LAVA : '*');
+# else
+ showexpl(bp->b_y, bp->b_x, '*');
+# endif
+ switch (Maze[bp->b_y][bp->b_x]) {
+ case LEFTS:
+ case RIGHT:
+ case ABOVE:
+ case BELOW:
+# ifdef FLY
+ case FLYER:
+# endif
+ pp = play_at(bp->b_y, bp->b_x);
+ message(pp, "You've been slimed.");
+ checkdam(pp, bp->b_owner, bp->b_score, MINDAM, bp->b_type);
+ break;
+ case SHOT:
+ case GRENADE:
+ case SATCHEL:
+ case BOMB:
+# ifdef DRONE
+ case DSHOT:
+# endif
+ explshot(next, bp->b_y, bp->b_x);
+ explshot(Bullets, bp->b_y, bp->b_x);
+ break;
+ }
+
+ if (--bp->b_charge <= 0) {
+ free((char *) bp);
+ return;
+ }
+
+ dirmask = 0;
+ count = 0;
+ switch (bp->b_face) {
+ case LEFTS:
+ if (!iswall(bp->b_y, bp->b_x - 1))
+ dirmask |= WEST, count++;
+ if (!iswall(bp->b_y - 1, bp->b_x))
+ dirmask |= NORTH, count++;
+ if (!iswall(bp->b_y + 1, bp->b_x))
+ dirmask |= SOUTH, count++;
+ if (dirmask == 0)
+ if (!iswall(bp->b_y, bp->b_x + 1))
+ dirmask |= EAST, count++;
+ break;
+ case RIGHT:
+ if (!iswall(bp->b_y, bp->b_x + 1))
+ dirmask |= EAST, count++;
+ if (!iswall(bp->b_y - 1, bp->b_x))
+ dirmask |= NORTH, count++;
+ if (!iswall(bp->b_y + 1, bp->b_x))
+ dirmask |= SOUTH, count++;
+ if (dirmask == 0)
+ if (!iswall(bp->b_y, bp->b_x - 1))
+ dirmask |= WEST, count++;
+ break;
+ case ABOVE:
+ if (!iswall(bp->b_y - 1, bp->b_x))
+ dirmask |= NORTH, count++;
+ if (!iswall(bp->b_y, bp->b_x - 1))
+ dirmask |= WEST, count++;
+ if (!iswall(bp->b_y, bp->b_x + 1))
+ dirmask |= EAST, count++;
+ if (dirmask == 0)
+ if (!iswall(bp->b_y + 1, bp->b_x))
+ dirmask |= SOUTH, count++;
+ break;
+ case BELOW:
+ if (!iswall(bp->b_y + 1, bp->b_x))
+ dirmask |= SOUTH, count++;
+ if (!iswall(bp->b_y, bp->b_x - 1))
+ dirmask |= WEST, count++;
+ if (!iswall(bp->b_y, bp->b_x + 1))
+ dirmask |= EAST, count++;
+ if (dirmask == 0)
+ if (!iswall(bp->b_y - 1, bp->b_x))
+ dirmask |= NORTH, count++;
+ break;
+ }
+ if (count == 0) {
+ /*
+ * No place to go. Just sit here for a while and wait
+ * for adjacent squares to clear out.
+ */
+ save_bullet(bp);
+ return;
+ }
+ if (bp->b_charge < count) {
+ /* Only bp->b_charge paths may be taken */
+ while (count > bp->b_charge) {
+ if (dirmask & WEST)
+ dirmask &= ~WEST;
+ else if (dirmask & EAST)
+ dirmask &= ~EAST;
+ else if (dirmask & NORTH)
+ dirmask &= ~NORTH;
+ else if (dirmask & SOUTH)
+ dirmask &= ~SOUTH;
+ count--;
+ }
+ }
+
+ i = bp->b_charge / count;
+ j = bp->b_charge % count;
+ if (dirmask & WEST) {
+ count--;
+ nbp = create_shot(bp->b_type, bp->b_y, bp->b_x - 1, LEFTS,
+ i, bp->b_size, bp->b_owner, bp->b_score, TRUE, SPACE);
+ move_slime(nbp, speed - 1, next);
+ }
+ if (dirmask & EAST) {
+ count--;
+ nbp = create_shot(bp->b_type, bp->b_y, bp->b_x + 1, RIGHT,
+ (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
+ bp->b_score, TRUE, SPACE);
+ move_slime(nbp, speed - 1, next);
+ }
+ if (dirmask & NORTH) {
+ count--;
+ nbp = create_shot(bp->b_type, bp->b_y - 1, bp->b_x, ABOVE,
+ (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
+ bp->b_score, TRUE, SPACE);
+ move_slime(nbp, speed - 1, next);
+ }
+ if (dirmask & SOUTH) {
+ count--;
+ nbp = create_shot(bp->b_type, bp->b_y + 1, bp->b_x, BELOW,
+ (count < j) ? i + 1 : i, bp->b_size, bp->b_owner,
+ bp->b_score, TRUE, SPACE);
+ move_slime(nbp, speed - 1, next);
+ }
+
+ free((char *) bp);
+}
+
+/*
+ * iswall:
+ * returns whether the given location is a wall
+ */
+iswall(y, x)
+register int y, x;
+{
+ if (y < 0 || x < 0 || y >= HEIGHT || x >= WIDTH)
+ return TRUE;
+ switch (Maze[y][x]) {
+ case WALL1:
+ case WALL2:
+ case WALL3:
+# ifdef REFLECT
+ case WALL4:
+ case WALL5:
+# endif
+# ifdef RANDOM
+ case DOOR:
+# endif
+# ifdef OOZE
+ case SLIME:
+# ifdef VOLCANO
+ case LAVA:
+# endif
+# endif
+ return TRUE;
+ }
+ return FALSE;
+}
+# endif
+
+/*
+ * zapshot:
+ * Take a shot out of the air.
+ */
+zapshot(blist, obp)
+register BULLET *blist, *obp;
+{
+ register BULLET *bp;
+ register FLAG explode;
+
+ explode = FALSE;
+ for (bp = blist; bp != NULL; bp = bp->b_next) {
+ if (bp->b_x != obp->b_x || bp->b_y != obp->b_y)
+ continue;
+ if (bp->b_face == obp->b_face)
+ continue;
+ explode = TRUE;
+ break;
+ }
+ if (!explode)
+ return;
+ explshot(blist, obp->b_y, obp->b_x);
+}
+
+/*
+ * explshot -
+ * Make all shots at this location blow up
+ */
+explshot(blist, y, x)
+register BULLET *blist;
+register int y, x;
+{
+ register BULLET *bp;
+
+ for (bp = blist; bp != NULL; bp = bp->b_next)
+ if (bp->b_x == x && bp->b_y == y) {
+ bp->b_expl = TRUE;
+ if (bp->b_owner != NULL)
+ message(bp->b_owner, "Shot intercepted");
+ }
+}
+
+/*
+ * play_at:
+ * Return a pointer to the player at the given location
+ */
+PLAYER *
+play_at(y, x)
+register int y, x;
+{
+ register PLAYER *pp;
+
+ for (pp = Player; pp < End_player; pp++)
+ if (pp->p_x == x && pp->p_y == y)
+ return pp;
+ fprintf(stderr, "driver: couldn't find player at (%d,%d)\n", x, y);
+ abort();
+ /* NOTREACHED */
+}
+
+/*
+ * opposite:
+ * Return TRUE if the bullet direction faces the opposite direction
+ * of the player in the maze
+ */
+opposite(face, dir)
+int face;
+char dir;
+{
+ switch (face) {
+ case LEFTS:
+ return (dir == RIGHT);
+ case RIGHT:
+ return (dir == LEFTS);
+ case ABOVE:
+ return (dir == BELOW);
+ case BELOW:
+ return (dir == ABOVE);
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ * is_bullet:
+ * Is there a bullet at the given coordinates? If so, return
+ * a pointer to the bullet, otherwise return NULL
+ */
+BULLET *
+is_bullet(y, x)
+register int y, x;
+{
+ register BULLET *bp;
+
+ for (bp = Bullets; bp != NULL; bp = bp->b_next)
+ if (bp->b_y == y && bp->b_x == x)
+ return bp;
+ return NULL;
+}
+
+/*
+ * fixshots:
+ * change the underlying character of the shots at a location
+ * to the given character.
+ */
+fixshots(y, x, over)
+register int y, x;
+char over;
+{
+ register BULLET *bp;
+
+ for (bp = Bullets; bp != NULL; bp = bp->b_next)
+ if (bp->b_y == y && bp->b_x == x)
+ bp->b_over = over;
+}
+
+/*
+ * find_under:
+ * find the underlying character for a bullet when it lands
+ * on another bullet.
+ */
+find_under(blist, bp)
+register BULLET *blist, *bp;
+{
+ register BULLET *nbp;
+
+ for (nbp = blist; nbp != NULL; nbp = nbp->b_next)
+ if (bp->b_y == nbp->b_y && bp->b_x == nbp->b_x) {
+ bp->b_over = nbp->b_over;
+ break;
+ }
+}
+
+/*
+ * mark_player:
+ * mark a player as under a shot
+ */
+mark_player(bp)
+register BULLET *bp;
+{
+ register PLAYER *pp;
+
+ for (pp = Player; pp < End_player; pp++)
+ if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
+ pp->p_undershot = TRUE;
+ break;
+ }
+}
+
+# ifdef BOOTS
+/*
+ * mark_boot:
+ * mark a boot as under a shot
+ */
+mark_boot(bp)
+register BULLET *bp;
+{
+ register PLAYER *pp;
+
+ for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
+ if (pp->p_y == bp->b_y && pp->p_x == bp->b_x) {
+ pp->p_undershot = TRUE;
+ break;
+ }
+}
+# endif
diff --git a/hunt/huntd/talk_ctl.h b/hunt/huntd/talk_ctl.h
new file mode 100644
index 00000000..2b73f820
--- /dev/null
+++ b/hunt/huntd/talk_ctl.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ * @(#)talk_ctl.h 5.2 (Berkeley) 3/13/86
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#ifdef TALK_43
+#include <protocols/talkd.h>
+#else
+
+#define NAME_SIZE 9
+#define TTY_SIZE 16
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+#define MAX_LIFE 60 /* max time daemon saves invitations */
+/* RING_WAIT should be 10's of seconds less than MAX_LIFE */
+#define RING_WAIT 30 /* time to wait before refreshing invitation */
+
+/* type values */
+#define LEAVE_INVITE 0
+#define LOOK_UP 1
+#define DELETE 2
+#define ANNOUNCE 3
+
+/* answer values */
+#define SUCCESS 0
+#define NOT_HERE 1
+#define FAILED 2
+#define MACHINE_UNKNOWN 3
+#define PERMISSION_DENIED 4
+#define UNKNOWN_REQUEST 5
+
+typedef struct ctl_response {
+ char type;
+ char answer;
+ int id_num;
+ struct sockaddr_in addr;
+} CTL_RESPONSE;
+
+typedef struct ctl_msg {
+ char type;
+ char l_name[NAME_SIZE];
+ char r_name[NAME_SIZE];
+ int id_num;
+ int pid;
+ char r_tty[TTY_SIZE];
+ struct sockaddr_in addr;
+ struct sockaddr_in ctl_addr;
+} CTL_MSG;
+#endif
+
+#include <errno.h>
+#ifdef LOG
+#include <syslog.h>
+#endif
+
+extern int errno;
+
+extern struct sockaddr_in daemon_addr;
+extern struct sockaddr_in ctl_addr;
+extern struct sockaddr_in my_addr;
+extern struct in_addr my_machine_addr;
+extern struct in_addr his_machine_addr;
+extern u_short daemon_port;
+extern int ctl_sockt;
+extern CTL_MSG msg;
+
+#ifdef LOG
+#define p_error(str) syslog(LOG_WARNING, "faketalk %s: %m", str)
+#else
+#define p_error(str) perror(str)
+#endif
diff --git a/hunt/huntd/terminal.c b/hunt/huntd/terminal.c
new file mode 100644
index 00000000..13d34850
--- /dev/null
+++ b/hunt/huntd/terminal.c
@@ -0,0 +1,110 @@
+/*
+ * Hunt
+ * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
+ * San Francisco, California
+ */
+
+# include "hunt.h"
+# define TERM_WIDTH 80 /* Assume terminals are 80-char wide */
+
+/*
+ * cgoto:
+ * Move the cursor to the given position on the given player's
+ * terminal.
+ */
+cgoto(pp, y, x)
+register PLAYER *pp;
+register int y, x;
+{
+ if (x == pp->p_curx && y == pp->p_cury)
+ return;
+ sendcom(pp, MOVE, y, x);
+ pp->p_cury = y;
+ pp->p_curx = x;
+}
+
+/*
+ * outch:
+ * Put out a single character.
+ */
+outch(pp, ch)
+register PLAYER *pp;
+char ch;
+{
+ if (++pp->p_curx >= TERM_WIDTH) {
+ pp->p_curx = 0;
+ pp->p_cury++;
+ }
+ (void) putc(ch, pp->p_output);
+}
+
+/*
+ * outstr:
+ * Put out a string of the given length.
+ */
+outstr(pp, str, len)
+register PLAYER *pp;
+register char *str;
+register int len;
+{
+ pp->p_curx += len;
+ pp->p_cury += (pp->p_curx / TERM_WIDTH);
+ pp->p_curx %= TERM_WIDTH;
+ while (len--)
+ (void) putc(*str++, pp->p_output);
+}
+
+/*
+ * clrscr:
+ * Clear the screen, and reset the current position on the screen.
+ */
+clrscr(pp)
+register PLAYER *pp;
+{
+ sendcom(pp, CLEAR);
+ pp->p_cury = 0;
+ pp->p_curx = 0;
+}
+
+/*
+ * ce:
+ * Clear to the end of the line
+ */
+ce(pp)
+PLAYER *pp;
+{
+ sendcom(pp, CLRTOEOL);
+}
+
+/*
+ * ref;
+ * Refresh the screen
+ */
+ref(pp)
+register PLAYER *pp;
+{
+ sendcom(pp, REFRESH);
+}
+
+/*
+ * sendcom:
+ * Send a command to the given user
+ */
+/* VARARGS2 */
+sendcom(pp, command, arg1, arg2)
+register PLAYER *pp;
+register int command;
+int arg1, arg2;
+{
+ (void) putc(command, pp->p_output);
+ switch (command & 0377) {
+ case MOVE:
+ (void) putc(arg1, pp->p_output);
+ (void) putc(arg2, pp->p_output);
+ break;
+ case ADDCH:
+ case READY:
+ (void) putc(arg1, pp->p_output);
+ break;
+ }
+}