Bring hunt in from OpenBSD. The best multi-player terminal game ever!
authorMatthew Dillon <dillon@dragonflybsd.org>
Tue, 2 Sep 2008 21:50:21 +0000 (21:50 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Tue, 2 Sep 2008 21:50:21 +0000 (21:50 +0000)
30 files changed:
games/Makefile
games/hunt/Makefile [new file with mode: 0644]
games/hunt/README [new file with mode: 0644]
games/hunt/README.protocol [new file with mode: 0644]
games/hunt/hunt/Makefile [new file with mode: 0644]
games/hunt/hunt/client.h [new file with mode: 0644]
games/hunt/hunt/connect.c [new file with mode: 0644]
games/hunt/hunt/display.c [new file with mode: 0644]
games/hunt/hunt/display.h [new file with mode: 0644]
games/hunt/hunt/hunt.6 [new file with mode: 0644]
games/hunt/hunt/hunt.c [new file with mode: 0644]
games/hunt/hunt/list.c [new file with mode: 0644]
games/hunt/hunt/list.h [new file with mode: 0644]
games/hunt/hunt/otto.c [new file with mode: 0644]
games/hunt/hunt/playit.c [new file with mode: 0644]
games/hunt/huntd/Makefile [new file with mode: 0644]
games/hunt/huntd/answer.c [new file with mode: 0644]
games/hunt/huntd/conf.c [new file with mode: 0644]
games/hunt/huntd/conf.h [new file with mode: 0644]
games/hunt/huntd/draw.c [new file with mode: 0644]
games/hunt/huntd/driver.c [new file with mode: 0644]
games/hunt/huntd/execute.c [new file with mode: 0644]
games/hunt/huntd/expl.c [new file with mode: 0644]
games/hunt/huntd/extern.c [new file with mode: 0644]
games/hunt/huntd/hunt.h [new file with mode: 0644]
games/hunt/huntd/huntd.6 [new file with mode: 0644]
games/hunt/huntd/makemaze.c [new file with mode: 0644]
games/hunt/huntd/server.h [new file with mode: 0644]
games/hunt/huntd/shots.c [new file with mode: 0644]
games/hunt/huntd/terminal.c [new file with mode: 0644]

index ebe4145..a07b6ae 100644 (file)
@@ -1,6 +1,6 @@
 #      @(#)Makefile    8.2 (Berkeley) 3/31/94
 # $FreeBSD: src/games/Makefile,v 1.16 1999/08/27 23:28:45 peter Exp $
-# $DragonFly: src/games/Makefile,v 1.2 2003/06/17 04:25:21 dillon Exp $
+# $DragonFly: src/games/Makefile,v 1.3 2008/09/02 21:50:17 dillon Exp $
 
 # XXX missing: chess ching monop [copyright]
 SUBDIR= adventure \
@@ -20,6 +20,7 @@ SUBDIR= adventure \
        grdc \
        hack \
        hangman \
+       hunt \
        larn \
        mille \
        morse \
diff --git a/games/hunt/Makefile b/games/hunt/Makefile
new file mode 100644 (file)
index 0000000..7acbf28
--- /dev/null
@@ -0,0 +1,7 @@
+#      $NetBSD: Makefile,v 1.1 1997/10/04 09:11:14 mrg Exp $
+#      $OpenBSD: Makefile,v 1.2 1999/01/21 05:47:37 d Exp $
+#      $DragonFly: src/games/hunt/Makefile,v 1.1 2008/09/02 21:50:18 dillon Exp $
+
+SUBDIR=        hunt huntd
+
+.include <bsd.subdir.mk>
diff --git a/games/hunt/README b/games/hunt/README
new file mode 100644 (file)
index 0000000..fe5d8da
--- /dev/null
@@ -0,0 +1,177 @@
+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.
+
+$DragonFly: src/games/hunt/README,v 1.1 2008/09/02 21:50:18 dillon Exp $
diff --git a/games/hunt/README.protocol b/games/hunt/README.protocol
new file mode 100644 (file)
index 0000000..05a338c
--- /dev/null
@@ -0,0 +1,273 @@
+
+THE HUNT PROTOCOL
+=================
+
+These are some notes on the traditional INET protocol between hunt(6) and 
+huntd(6) as divined from the source code.
+
+(In the original hunt, AF_UNIX sockets were used, but they are not 
+considered here.)
+
+The game of hunt is played with one server and several clients. The clients
+act as dumb 'graphics' clients in that they mostly only ever relay the
+user's keystrokes to the server, and the server usually only ever sends
+screen-drawing commands to the client. ie, the server does all the work.
+
+The game server (huntd) listens on three different network ports which 
+I'll refer to as W, S and P, described as follows:
+
+       W       well known UDP port (26740, or 'udp/hunt' in netdb)
+       S       statistics TCP port
+       P       game play TCP port
+
+The protocol on each port is different and are described separately in
+the following sections.
+
+Lines starting with "C:" and "S:" will indicate messages sent from the 
+client (hunt) or server (huntd) respectively.
+
+W - well known port
+-------------------
+       This server port is used only to query simple information about the 
+       game such as the port numbers of the other two ports (S and P),
+       and to find out how many players are still in the game.
+
+       All datagrams sent to (and possibly from) this UDP port consist of 
+       a single unsigned 16-bit integer, encoded in network byte order.
+
+       Server response datagrams should be sent to the source address
+       of the client request datagrams.
+
+       It is not useful to run multiple hunt servers on the one host
+       interface, each of which perhaps listen to the well known port and
+       respond appropriately. This is because clients will not be able to
+       disambiguate which game is which.
+
+       It is reasonable (and expected) to have servers listen to a 
+       broadcast or multicast network address and respond, since the
+       clients can extract a particular server's network address from
+       the reply packet's source field.
+
+    Player port request
+
+       A client requests the game play port P with the C_PLAYER message.
+       This is useful for clients broadcasting for any available games. eg:
+               
+               C: {uint16: 0 (C_PLAYER)}
+               S: {uint16: P (TCP port number for the game play port)}
+
+       The TCP address of the game play port should be formed from the
+       transmitted port number and the source address as received by
+       the client.
+
+    Monitor port request
+
+       A client can request the game play port P with the C_MONITOR message.
+       However, the server will NOT reply if there are no players in
+       the game. This is useful for broadcasting for 'active' games. eg:
+
+               C: {uint16: 1 (C_MONITOR)}
+               S: {uint16: P (TCP port number for the game play port)}
+
+    Message port request
+
+       If the server receives the C_MESSAGE message it will
+       respond with the number of players currently in its game, unless
+       there are 0 players, in which case it remains silent. This
+       is used when a player wishes to send a text message to all other
+       players, but doesn't want to connect if the game is over. eg:
+
+               C: {uint16: 2 (C_MESSAGE)}
+               S: {uint16: n (positive number of players)}
+
+    Statistics port request
+
+       The server's statistics port is queried with the C_SCORES message.
+       eg:
+
+               C: {uint16: 3 (C_SCORES)}
+               S: {uint16: S (TCP port number for the statistics port)}
+
+
+S - statistics port
+-------------------
+       The statistics port accepts a TCP connection, and keeps
+       it alive for long enough to send a text stream to the client.
+       This text consists of the game statistics. Lines in the
+       text message are terminated with the \n (LF) character. 
+
+               C: <connect>
+               S: <accept>
+               S: {char[]: lines of text, each terminated with <LF>}
+               S: <close>
+
+       The client is not to send any data to the server with this
+       connection.
+
+P - game play port
+------------------
+       This port provides the TCP channel for the main game play between
+       the client and the server.
+
+       All integers are unsigned, 32-bit and in network byte order.
+       All fixed sized octet strings are ASCII encoded, NUL terminated.
+
+    Initial connection
+
+       The initial setup protocol between the client and server is as follows.
+       The client sends some of its own details, and then the server replies
+       with the version number of the server (currently (uint32)-1).
+
+               C: <connect>
+               S: <accept>
+               C: {uint32:   uid}
+               C: {char[20]: name}
+               C: {char[1]:  team}
+               C: {uint32:   'enter status'}
+               C: {char[20]: ttyname}
+               C: {uint32:   'connect mode'}
+               S: {uint32:   server version (-1)}
+
+       If the 'connect mode' is C_MESSAGE (2) then the server will wait
+       for a single packet (no longer than 1024 bytes) containing
+       a text message to be displayed to all players. (The message is not
+       nul-terminated.)
+
+               C: {char[]:     client's witty message of abuse}
+               S: <close>
+
+       The only other valid 'connect mode's are C_MONITOR and C_PLAYER.
+       The server will attempt to allocate a slot for the client. 
+       If allocation fails, the server will reply immediately with 
+       "Too many monitors\n" or "Too many players\n', e.g.:
+
+               S: Too many players<LF>
+               S: <close>
+
+       The 'enter status' integer is one of the following:
+
+               1 (Q_CLOAK)     the player wishes to enter cloaked
+               2 (Q_FLY)       the player wishes to enter flying
+               3 (Q_SCAN)      the player wishes to enter scanning
+
+       Any other value indicates that the player wishes to enter in
+       'normal' mode.
+
+       A team value of 32 (space character) means no team, otherwise
+       it is the ASCII value of a team's symbol.
+
+       On successful allocation, the server will immediately enter the 
+       following phase of the protocol.
+
+    Game play protocol
+
+       The client provides a thin 'graphical' client to the server, and
+       only ever relays keystrokes typed by the user:
+
+               C: {char[]:     user keystrokes}
+
+       Each character must be sent by the client as soon as it is typed.
+
+
+       The server only ever sends screen drawing commands to the client.
+       The server assumes the initial state of the client is a clear
+       80x24 screen with the cursor at the top left (position y=0, x=0)
+
+           Literal character   225 (ADDCH)
+
+               S: {uint8: 225} {uint8: c}
+
+               The client must draw the character with ASCII value c
+               at the cursor position, then advance the cursor to the right.
+               If the cursor goes past the rightmost column of the screen,
+               it wraps, moving to the first column of the next line down.
+               The cursor should never be advanced past the bottom row.
+
+               (ADDCH is provided as an escape prefix.)
+
+           Cursor motion       237 (MOVE)
+
+               S: {uint8: 237} {uint8: y} {uint8: x}
+
+               The client must move its cursor to the absolute screen
+               location y, x, where y=0 is the top of the screen and
+               x=0 is the left of the screen.
+
+           Refresh screen      242 (REFRESH)
+
+               S: {uint8: 242}
+
+               This indicates to the client that a burst of screen
+               drawing has ended. Typically the client will flush its
+               own drawing output so that the user can see the results.
+
+               Refreshing is the only time that the client must
+               ensure that the user can see the current screen. (This
+               is intended for use with curses' refresh() function.)
+
+           Clear to end of line 227 (CLRTOEOL)
+
+               S: {uint8: 227}
+
+               The client must replace all columns underneath and
+               to the right of the cursor (on the one row) with 
+               space characters. The cursor must not move.
+
+           End game            229 (ENDWIN)
+
+               S: {uint8: 229} {uint8: 32}
+               S,C: <close>
+
+               S: {uint8: 229} {uint8: 236}
+               S,C: <close>
+
+               The client and server must immediately close the connection.
+               The client should also refresh the screen.
+               If the second octet is 236 (LAST_PLAYER), then 
+               the client should give the user an opportunity to quickly 
+               re-enter the game. Otherwise the client should quit.
+
+           Clear screen        195 (CLEAR)
+
+               S: {uint8: 195}
+
+               The client must erase all characters from the screen
+               and move the cursor to the top left (x=0, y=0).
+
+           Redraw screen       210 (REDRAW)
+
+               S: {uint8: 210}
+
+               The client should attempt to re-draw its screen.
+
+           Audible bell        226 (BELL)
+
+               S: {uint8: 226}
+
+               The client should generate a short audible tone for
+               the user.
+
+           Server ready        231 (READY)
+
+               S: {uint8: 231} {uint8: n}
+
+               The client must refresh its screen.
+
+               The server indicates to the client that it has
+               processed n of its characters in order, and is ready
+               for more commands. This permits the client to 
+               synchronise user actions with server responses if need be.
+
+           Characters other than the above.
+
+               S: {uint8: c}
+
+               The client must draw the character with ASCII value c
+               in the same way as if it were preceded with ADDCH
+               (see above).
+
+
+David Leonard, 1999.
+
+$OpenBSD: README.protocol,v 1.1 1999/12/12 14:51:03 d Exp $
+$DragonFly: src/games/hunt/README.protocol,v 1.1 2008/09/02 21:50:18 dillon Exp $
diff --git a/games/hunt/hunt/Makefile b/games/hunt/hunt/Makefile
new file mode 100644 (file)
index 0000000..a7e4356
--- /dev/null
@@ -0,0 +1,14 @@
+#      $NetBSD: Makefile,v 1.5 1998/02/18 22:37:31 jtc Exp $
+#      $OpenBSD: Makefile,v 1.7 2002/05/23 18:43:00 deraadt Exp $
+#      $DragonFly: src/games/hunt/hunt/Makefile,v 1.1 2008/09/02 21:50:20 dillon Exp $
+
+PROG=  hunt
+SRCS=  connect.c hunt.c otto.c playit.c display.c list.c
+MAN=   hunt.6
+LDADD= -lcurses
+DPADD= ${LIBCURSES}
+
+CFLAGS+=-I${.CURDIR}/../huntd
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/games/hunt/hunt/client.h b/games/hunt/hunt/client.h
new file mode 100644 (file)
index 0000000..b612c3b
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: client.h,v 1.4 2003/06/17 00:44:21 pjanzen Exp $
+ * $DragonFly: src/games/hunt/hunt/client.h,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+/* hunt.c */
+extern FLAG    Am_monitor;             /* -m flag */
+extern FLAG    no_beep;                /* -b flag */
+extern char *  Send_message;           /* -w message */
+extern int     Socket;                 /* connection to server */
+extern char     map_key[256];          /* HUNT envvar */
+
+void   bad_con(void);
+void   bad_ver(void);
+void   intr(int);
+
+/* connect.c */
+void   do_connect(char *, u_int8_t, u_int32_t);
+
+/* playit.c */
+void   playit(void);
+void   do_message(void);
+int    quit(int);
+
+/* otto.c */
+extern int     Otto_mode;
+int    otto(int, int, char, char *, size_t);
+int    otto_quit(int);
diff --git a/games/hunt/hunt/connect.c b/games/hunt/hunt/connect.c
new file mode 100644 (file)
index 0000000..74b1de7
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: connect.c,v 1.6 2003/06/11 08:45:24 pjanzen Exp $
+ * $NetBSD: connect.c,v 1.3 1997/10/11 08:13:40 lukem Exp $
+ * $DragonFly: src/games/hunt/hunt/connect.c,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "hunt.h"
+#include "client.h"
+
+void
+do_connect(name, team, enter_status)
+       char *          name;
+       u_int8_t        team;
+       u_int32_t       enter_status;
+{
+       u_int32_t       uid;
+       u_int32_t       mode;
+       const char *    Ttyname;
+       char            buf[NAMELEN];
+
+       if (Send_message != NULL)
+               mode = C_MESSAGE;
+       else if (Am_monitor)
+               mode = C_MONITOR;
+       else
+               mode = C_PLAYER;
+
+       Ttyname = ttyname(STDOUT_FILENO);
+       if (Ttyname == NULL)
+               Ttyname = "not a tty";
+       memset(buf, '\0', sizeof buf);
+       (void) strlcpy(buf, Ttyname, sizeof buf);
+
+       uid = htonl(getuid());
+       enter_status = htonl(enter_status);
+       mode = htonl(mode);
+
+       (void) write(Socket, &uid, sizeof uid);
+       (void) write(Socket, name, NAMELEN);
+       (void) write(Socket, &team, sizeof team);
+       (void) write(Socket, &enter_status, sizeof enter_status);
+       (void) write(Socket, buf, NAMELEN);
+       (void) write(Socket, &mode, sizeof mode);
+}
diff --git a/games/hunt/hunt/display.c b/games/hunt/hunt/display.c
new file mode 100644 (file)
index 0000000..14a81df
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * Display abstraction.
+ * David Leonard <d@openbsd.org>, 1999. Public domain.
+ *
+ * $OpenBSD: display.c,v 1.4 2002/02/19 19:39:36 millert Exp $
+ * $DragonFly: src/games/hunt/hunt/display.c,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+#define USE_CURSES
+
+#include <sys/cdefs.h>
+#include "display.h"
+
+#if !defined(USE_CURSES)
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>   
+#define _USE_OLD_CURSES_
+#include <curses.h>
+#include <err.h>
+#include "hunt.h"
+
+static struct termios saved_tty;
+
+char   screen[SCREEN_HEIGHT][SCREEN_WIDTH2];
+char   blanks[SCREEN_WIDTH];
+int    cur_row, cur_col;
+
+/*
+ * tstp:
+ *      Handle stop and start signals
+ */
+static void
+tstp(dummy)
+        int dummy;
+{
+        int     y, x;
+
+        y = cur_row;
+        x = cur_col;
+        mvcur(cur_row, cur_col, HEIGHT, 0);
+        cur_row = HEIGHT;
+        cur_col = 0;
+        _puts(VE);
+        _puts(TE);
+        (void) fflush(stdout);
+        tcsetattr(0, TCSADRAIN, &__orig_termios);
+        (void) kill(getpid(), SIGSTOP);
+        (void) signal(SIGTSTP, tstp);
+        tcsetattr(0, TCSADRAIN, &saved_tty);
+        _puts(TI);
+        _puts(VS);
+        cur_row = y;
+        cur_col = x;
+        _puts(tgoto(CM, cur_row, cur_col));
+        display_redraw_screen();
+        (void) fflush(stdout);
+}
+
+/*
+ * display_open:
+ *     open the display
+ */
+void
+display_open()
+{
+       char *term;
+
+       if (!isatty(0) || (term = getenv("TERM")) == NULL)
+               errx(1, "no terminal type");
+
+        gettmode();
+        (void) setterm(term);
+        (void) noecho();  
+        (void) cbreak();
+        tcgetattr(0, &saved_tty);
+        _puts(TI);
+        _puts(VS);
+#ifdef SIGTSTP
+        (void) signal(SIGTSTP, tstp);
+#endif
+}
+
+/*
+ * display_beep:
+ *     beep
+ */
+void
+display_beep()
+{
+       (void) putchar('\a');
+}
+
+/*
+ * display_refresh:
+ *     sync the display
+ */
+void
+display_refresh()
+{
+       (void) fflush(stdout);
+}
+
+/*
+ * display_clear_eol:
+ *     clear to end of line, without moving cursor
+ */
+void
+display_clear_eol()
+{
+        if (CE != NULL)
+                tputs(CE, 1, __cputchar);
+        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);
+}
+
+/*
+ * display_putchar:
+ *     put one character on the screen, move the cursor right one,
+ *     with wraparound
+ */
+void
+display_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;
+        }
+}
+
+/*
+ * display_put_str:
+ *     put a string of characters on the screen
+ */
+void
+display_put_str(const char *s)
+{
+       for( ; *s; s++)
+               display_put_ch(*s);
+}
+
+/*
+ * display_clear_the_screen:
+ *     clear the screen; move cursor to top left
+ */
+void
+display_clear_the_screen()
+{
+        int     i;
+
+        if (blanks[0] == '\0')   
+                for (i = 0; i < SCREEN_WIDTH; i++)
+                        blanks[i] = ' ';
+  
+        if (CL != NULL) {
+                tputs(CL, LINES, __cputchar);
+                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;
+                        display_clear_eol();
+                }
+                mvcur(cur_row, cur_col, 0, 0);
+        }
+        cur_row = cur_col = 0;
+}
+
+/*
+ * display_move:
+ *     move the cursor
+ */
+void
+display_move(y, x)
+       int y, x;
+{
+       mvcur(cur_row, cur_col, y, x);
+       cur_row = y;
+       cur_col = x;
+}
+
+/*
+ * display_getyx:
+ *     locate the cursor
+ */
+void
+display_getyx(yp, xp)
+       int *yp, *xp;
+{
+       *xp = cur_col;
+       *yp = cur_row;
+}
+
+/*
+ * display_end:
+ *     close the display
+ */
+void
+display_end()
+{
+       tcsetattr(0, TCSADRAIN, &__orig_termios);
+       _puts(VE);
+       _puts(TE);
+}
+
+/*
+ * display_atyx:
+ *     return a character from the screen
+ */
+char
+display_atyx(y, x)
+       int y, x;
+{
+       return screen[y][x];
+}
+
+/*
+ * display_redraw_screen:
+ *     redraw the screen
+ */
+void
+display_redraw_screen()
+{
+       int i;
+
+        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);
+}
+
+#else /* CURSES */ /* --------------------------------------------------- */
+
+#include <curses.h>
+#include "hunt.h"
+
+void
+display_open()
+{
+        initscr();
+        (void) noecho(); 
+        (void) cbreak();  
+}
+
+void
+display_beep()
+{
+       beep();
+}
+
+void
+display_refresh()
+{
+       refresh();
+}
+
+void
+display_clear_eol()
+{
+       clrtoeol();
+}
+
+void
+display_put_ch(c)
+       char c;
+{
+       addch(c);
+}
+
+void
+display_put_str(const char *s)
+{
+       addstr(s);
+}
+
+void
+display_clear_the_screen()
+{
+        clear();
+        move(0, 0);
+        display_refresh();
+}
+
+void
+display_move(y, x)
+       int y, x;
+{
+       move(y, x);
+}
+
+void
+display_getyx(yp, xp)
+       int *yp, *xp;
+{
+       getyx(stdscr, *yp, *xp);
+}
+
+void
+display_end()
+{
+       endwin();
+}
+
+char
+display_atyx(y, x)
+       int y, x;
+{
+       int oy, ox;
+       char c;
+
+       display_getyx(&oy, &ox);
+       c = mvwinch(stdscr, y, x) & 0x7f;
+       display_move(oy, ox);
+       return (c);
+}
+
+void
+display_redraw_screen()
+{
+       clearok(stdscr, TRUE);
+       touchwin(stdscr);
+}
+
+int
+display_iserasechar(ch)
+       char ch;
+{
+       return ch == erasechar();
+}
+
+int
+display_iskillchar(ch)
+       char ch;
+{
+       return ch == killchar();
+}
+
+#endif
diff --git a/games/hunt/hunt/display.h b/games/hunt/hunt/display.h
new file mode 100644 (file)
index 0000000..a6f7920
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * David Leonard <d@openbsd.org>, 1999.  Public domain.
+ * 
+ * $OpenBSD: display.h,v 1.3 2003/06/17 00:36:36 pjanzen Exp $
+ * $DragonFly: src/games/hunt/hunt/display.h,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+void display_open(void);
+void display_beep(void);
+void display_refresh(void);
+void display_clear_eol(void);
+void display_put_ch(char);
+void display_put_str(const char *);
+void display_clear_the_screen(void);
+void display_move(int, int);
+void display_getyx(int *, int *);
+void display_end(void);
+char display_atyx(int, int);
+void display_redraw_screen(void);
+int  display_iskillchar(char);
+int  display_iserasechar(char);
+
+extern int     cur_row, cur_col;
diff --git a/games/hunt/hunt/hunt.6 b/games/hunt/hunt/hunt.6
new file mode 100644 (file)
index 0000000..913587e
--- /dev/null
@@ -0,0 +1,502 @@
+.\"    $NetBSD: hunt.6,v 1.3 1997/10/10 16:32:30 lukem Exp $
+.\"    $OpenBSD: hunt.6,v 1.19 2007/05/31 19:19:17 jmc Exp $
+.\"    $DragonFly: src/games/hunt/hunt/hunt.6,v 1.1 2008/09/02 21:50:20 dillon Exp $
+.\"
+.\" 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.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd $Mdocdate$
+.Dt HUNT 6
+.Os
+.Sh NAME
+.Nm hunt
+.Nd a multi-player multi-terminal game
+.Sh SYNOPSIS
+.Nm hunt
+.Op Fl bcfmqSs
+.Op Fl n Ar name
+.Op Fl p Ar port
+.Op Fl t Ar team
+.Op Fl w Ar message
+.Oo
+.Op Fl h
+.Ar host
+.Oc
+.Sh DESCRIPTION
+The object of the game
+.Nm
+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
+.Fl m
+flag is given,
+you enter the game as a monitor
+.Po
+you can see the action but you cannot play
+.Pc .
+.Pp
+.Nm
+looks for an active game on the local network.
+The location of the game may be specified by giving the
+.Ar host
+argument.
+This presupposes that a hunt game is already running on that host:  see
+.Xr huntd 6
+for details on how to set up a game on a specific host.
+If more than one game is found, you may pick which game to play in.
+If no games are found,
+.Nm
+will exit.
+.Pp
+If the
+.Fl q
+flag is given,
+.Nm
+queries the local network
+.Pq or specific host
+and reports on all active games found.
+This is useful for shell startup scripts, e.g., csh's
+.Pa .login .
+.Pp
+The player name may be specified on the command line by using the
+.Fl n
+option.
+.Pp
+The
+.Fl c ,
+.Fl s ,
+and
+.Fl f
+options are for entering the game cloaked, scanning, or flying, respectively.
+.Pp
+The
+.Fl b
+option turns off beeping when you reach the typeahead limit.
+.Pp
+The
+.Fl t
+option aids team play by making everyone else on your team
+appear as the team name.
+A team name is a single digit to avoid conflicting with other characters
+used in the game.
+Use a team name consisting of a single space
+.Pq Sq \
+to remain on your own.
+.Pp
+The
+.Fl p
+.Ar port
+option allows the rendezvous port number to be set.
+.Pp
+The
+.Fl w
+.Ar 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
+.Dq 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
+.Ic w
+to write a message before continuing or
+.Ic o
+to change how you enter the game
+.Pq cloaked, scanning, or flying .
+.Sh "PLAYING HINTS"
+.Nm
+only works on 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
+.Po
+the
+.Ql \&*
+in front of the name
+.Pc ,
+who's cloaked
+.Po
+the
+.Ql \&+
+in front of the name
+.Pc ,
+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
+.Nm
+uses the same keys to move as
+.Xr vi 1
+does, i.e.,
+.Ic h ,
+.Ic j ,
+.Ic k ,
+and
+.Ic 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
+.Po
+i.e.,
+.Ic HJKL
+.Pc .
+You can only fire or throw things in the direction you're facing.
+.Pp
+Other commands are:
+.Bl -tag -width Ic -compact
+.It Ic f No or Ic 1
+Fire a bullet
+.Pq Takes 1 charge
+.It Ic g No or Ic 2
+Throw grenade
+.Pq Takes 9 charges
+.It Ic F No or Ic 3
+Throw satchel charge
+.Pq Takes 25 charges
+.It Ic G No or Ic 4
+Throw bomb
+.Pq Takes 49 charges
+.It Ic 5
+Throw big bomb
+.Pq Takes 81 charges
+.It Ic 6
+Throw even bigger bomb
+.Pq Takes 121 charges
+.It Ic 7
+Throw even more big bomb
+.Pq Takes 169 charges
+.It Ic 8
+Throw even more bigger bomb
+.Pq Takes 225 charges
+.It Ic 9
+Throw very big bomb
+.Pq Takes 289 charges
+.It Ic 0
+Throw very, very big bomb
+.Pq Takes 361 charges
+.It Ic @
+Throw biggest bomb
+.Pq Takes 441 charges
+.It Ic o
+Throw small slime
+.Pq Takes 5 charges
+.It Ic O
+Throw big slime
+.Pq Takes 10 charges
+.It Ic p
+Throw bigger slime
+.Pq Takes 15 charges
+.It Ic P
+Throw biggest slime
+.Pq Takes 20 charges
+.It Ic s
+Scan
+.Pq show where other players are
+.Pq Takes 1 charge
+.It Ic c
+Cloak
+.Pq hide from scanners
+.Pq Takes 1 charge
+.It Ic ^L
+Redraw screen
+.It Ic q
+Quit
+.El
+.Pp
+The symbols on the screen are:
+.Bl -tag -width Ic -compact
+.It Li \&- \&| \&+
+walls
+.It Li \&/ \e
+diagonal
+.Pq deflecting
+walls
+.It Li #
+doors
+.Pq dispersion walls
+.It Li ;
+small mine
+.It Li g
+large mine
+.It Li :
+bullet
+.It Li o
+grenade
+.It Li O
+satchel charge
+.It Li @
+bomb
+.It Li s
+small slime
+.It Li $
+big slime
+.It Li > < ^ v
+you, facing right, left, up, or down
+.It Li } { i \&!
+other players facing right, left, up, or down
+.It \&*
+explosion
+.It Li \&\e|/
+.It Li \&-*-
+grenade and large mine explosion
+.It Li \&/|\e
+.El
+.Pp
+Other helpful hints:
+.Bl -bullet -compact
+.It
+You can only fire in the direction you are facing.
+.It
+You can only fire three shots in a row before the gun must cool off.
+.It
+Shots move 5 times faster than you do.
+.It
+To stab someone,
+you face that player and move at them.
+.It
+Stabbing does 2 points worth of damage and shooting does 5 points.
+.It
+Slime does 5 points of damage each time it hits.
+.It
+You start with 15 charges and get 5 more every time a player enters
+or re-enters.
+.It
+Grenade explosions cover a 3 by 3 area, each larger bomb cover a
+correspondingly larger area
+.Po
+ranging from 5 by 5 to 21 by 21
+.Pc .
+All explosions are centered around the square the shot hits and
+do the most damage in the center.
+.It
+Slime affects all squares it oozes over.
+The number of squares is equal to the number of charges used,
+multiplied by
+.Va slimefactor
+as set by
+.Xr huntd 6
+.Pq default 3 .
+.It
+One small mine and one large mine are 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.
+.It
+You cannot see behind you.
+.It
+Cloaking consumes 1 ammo charge per 20 of your moves.
+.It
+Scanning consumes 1 ammo charge per (20 \(mu the number of players)
+of other player moves.
+.It
+Turning on cloaking turns off scanning \(em turning on scanning turns off
+cloaking.
+.It
+When you kill someone,
+you get 2 more damage capacity points and 2 damage points get taken away.
+.It
+Maximum typeahead is 5 characters.
+.It
+A shot destroys normal
+.Pq i.e., non-diagonal, non-door
+walls.
+.It
+Diagonal walls deflect shots and change orientation.
+.It
+Doors disperse shots in random directions
+.Pq up, down, left, right .
+.It
+Diagonal walls and doors cannot be destroyed by direct shots but may
+be destroyed by an adjacent grenade explosion.
+.It
+Slime goes around walls, not through them.
+.It
+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
+.Po
+up to 20 percent of the amount of damage already sustained
+.Pc ;
+i.e.,
+the less damage he had, the more nimble he is and
+therefore less likely to hurt himself on landing.
+.\"It
+.\"There is a volcano close to the center of the maze which goes off
+.\"close to every 30 deaths.
+.It
+Every 30 deaths or so, a
+.Ql \&?
+will appear.
+It is a wandering bomb which will explode when it hits someone, or
+when it is slimed.
+.It
+If no one moves, everything stands still.
+But see the
+.Va simstep
+configuration variable in
+.Xr huntd 6
+.It
+The environment variable
+.Ev HUNT
+is checked to get the player name.
+If you don't have this variable set,
+.Nm
+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:
+.Dl setenv HUNT "name=Sneaky,team=1,cloak,mapkey=zoFfGg1f2g3F4G"
+sets the player name to Sneaky,
+sets the team to one,
+sets the enter game attribute to cloaked,
+and the maps
+.Ic z No to Ic o ,
+.Ic F No to Ic f ,
+.Ic G No to Ic g ,
+.Ic 1 No to Ic f ,
+.Ic 2 No to Ic g ,
+.Ic 3 No to Ic F ,
+and
+.Ic 4 No to Ic G .
+The
+.Ic mapkey
+option must be last.
+Other options are:
+.Ic scan , fly , nobeep ,
+.Ic port= Ns Ar string ,
+.Ic host= Ns Ar string ,
+and
+.Ic message= Ns Ar string
+\(em which correspond to the command line options.
+String options cannot contain commas since commas
+are used to separate options.
+.It
+It's a boring game if you're the only one playing.
+.El
+.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
+.Nm hunt .
+.\" .Pp
+.\" .Nm
+.\" normally drives up the load average to be approximately
+.\" (number_of_players + 0.5) greater than it would be without a
+.\" .Nm
+.\" game executing.
+.Sh STATISTICS
+The
+.Fl S
+option fetches the current game statistics.
+.Pp
+Two groups of statistics are presented: the first group of statistics is
+that of the clients currently connected to the game, and is reset each
+time the client rejoins, while the second group of statistics is on all
+players
+.Pq dead or alive
+by name, and collected over the lifetime of the
+game daemon.
+.Pp
+The meaning of the column headings are as follows:
+.Bl -tag -width No -compact -offset
+.It Score
+the player's last score
+.It Ducked
+how many shots a player ducked
+.It Absorb
+how many shots a player absorbed
+.It Faced
+how many shots were fired at player's face
+.It Shot
+how many shots were fired at player
+.It Robbed
+how many of player's shots were absorbed
+.It Missed
+how many of player's shots were ducked
+.It SlimeK
+how many slime kills player had
+.It Enemy
+how many enemies were killed
+.It Friend
+how many friends were killed
+.Pq self and same team
+.It Deaths
+how many times player died
+.It Still
+how many times player died without typing in any commands
+.It Saved
+how many times a shot/bomb would have killed player if he hadn't
+ducked or absorbed it
+.It Connect
+current connection state(s) of player:
+.Sq p
+for playing,
+.Sq m
+for monitoring
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /usr/games/huntd
+game coordinator
+.El
+.Sh SEE ALSO
+.Xr huntd 6
+.Sh AUTHORS
+Conrad Huang, Ken Arnold, and Greg Couch;
+.br
+University of California, San Francisco, Computer Graphics Lab
+.Sh BUGS
+To keep up the pace, not everything is as realistic as possible.
+.Pp
+The historic behaviour of
+.Nm
+automatically starting
+.Xr huntd 6
+is no longer supported.
+.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.
diff --git a/games/hunt/hunt/hunt.c b/games/hunt/hunt/hunt.c
new file mode 100644 (file)
index 0000000..6fb8bcc
--- /dev/null
@@ -0,0 +1,687 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: hunt.c,v 1.13 2008/03/17 09:17:56 sobrado Exp $
+ * $NetBSD: hunt.c,v 1.8 1998/09/13 15:27:28 hubertf Exp $
+ * $DragonFly: src/games/hunt/hunt/hunt.c,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <curses.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sockio.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <arpa/inet.h>
+
+#include "hunt.h"
+#include "display.h"
+#include "client.h"
+#include "list.h"
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
+FLAG   Am_monitor = FALSE;
+int    Socket;
+char   map_key[256];                   /* what to map keys to */
+FLAG   no_beep = FALSE;
+char   *Send_message = NULL;
+
+static char    *Sock_host;
+static char    *use_port;
+static FLAG    Query_driver = FALSE;
+static FLAG    Show_scores = FALSE;
+static struct sockaddr Daemon;
+
+
+static char    name[NAMELEN];
+static char    team = '-';
+
+static int     in_visual;
+
+static void    dump_scores(void);
+static long    env_init(long);
+static void    fill_in_blanks(void);
+static void    leave(int, const char *) __attribute__((__noreturn__));
+static void    sigterm(int);
+static int     find_driver(void);
+
+/*
+ * main:
+ *     Main program for local process
+ */
+int
+main(int ac, char **av)
+{
+       int             c;
+       long            enter_status;
+       int             option;
+       struct servent  *se;
+
+       enter_status = env_init((long) Q_CLOAK);
+       while ((c = getopt(ac, av, "Sbcfh:l:mn:op:qst:w:")) != -1) {
+               switch (c) {
+               case 'l':       /* rsh compatibility */
+               case 'n':
+                       (void) strlcpy(name, optarg, sizeof name);
+                       break;
+               case 't':
+                       team = *optarg;
+                       if (!isdigit(team) && team != ' ') {
+                               warnx("Team names must be numeric or space");
+                               team = '-';
+                       }
+                       break;
+               case 'o':
+                       Otto_mode = TRUE;
+                       break;
+               case 'm':
+                       Am_monitor = TRUE;
+                       break;
+               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;
+                       Server_port = atoi(use_port);
+                       break;
+               case 'c':
+                       enter_status = Q_CLOAK;
+                       break;
+               case 'f':
+                       enter_status = Q_FLY;
+                       break;
+               case 's':
+                       enter_status = Q_SCAN;
+                       break;
+               case 'b':
+                       no_beep = !no_beep;
+                       break;
+               default:
+               usage:
+                       fputs("usage: hunt [-bcfmqSs] [-n name] [-p port] "
+                           "[-t team] [-w message] [[-h] host]\n",
+                           stderr);
+                       exit(1);
+               }
+       }
+       if (optind + 1 < ac)
+               goto usage;
+       else if (optind + 1 == ac)
+               Sock_host = av[ac - 1];
+
+       if (Server_port == 0) {
+               se = getservbyname("hunt", "udp");
+               if (se != NULL)
+                       Server_port = ntohs(se->s_port);
+               else
+                       Server_port = HUNT_PORT;
+       }
+
+       if (Show_scores) {
+               dump_scores();
+               exit(0);
+       }
+
+       if (Query_driver) {
+               struct driver           *driver;
+
+               probe_drivers(C_MESSAGE, Sock_host);
+               while ((driver = next_driver()) != NULL) {
+                       printf("%d player%s hunting on %s!\n",
+                           driver->response,
+                           (driver->response == 1) ? "" : "s",
+                           driver_name(driver));
+                       if (Sock_host)
+                               break;
+               }
+               exit(0);
+       }
+       if (Otto_mode) {
+               if (Am_monitor)
+                       errx(1, "otto mode incompatible with monitor mode");
+               (void) strlcpy(name, "otto", sizeof name);
+               team = ' ';
+       } else
+               fill_in_blanks();
+
+       (void) fflush(stdout);
+       display_open();
+       in_visual = TRUE;
+       if (LINES < SCREEN_HEIGHT || COLS < SCREEN_WIDTH) {
+               errno = 0;
+               leave(1, "Need a larger window");
+       }
+       display_clear_the_screen();
+       (void) signal(SIGINT, intr);
+       (void) signal(SIGTERM, sigterm);
+       /* (void) signal(SIGPIPE, SIG_IGN); */
+
+       Daemon.sa_len = 0;
+    ask_driver:
+       while (!find_driver()) {
+               if (Am_monitor) {
+                       errno = 0;
+                       leave(1, "No one playing");
+               }
+
+               if (Sock_host == NULL) {
+                       errno = 0;
+                       leave(1, "huntd not running");
+               }
+
+               sleep(3);
+       }
+       Socket = -1;
+
+       for (;;) {
+               if (Socket != -1)
+                       close(Socket);
+
+               Socket = socket(Daemon.sa_family, SOCK_STREAM, 0);
+               if (Socket < 0)
+                       leave(1, "socket");
+
+               option = 1;
+               if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK,
+                   &option, sizeof option) < 0)
+                       warn("setsockopt loopback");
+
+               errno = 0;
+               if (connect(Socket, &Daemon, Daemon.sa_len) == -1)  {
+                       if (errno == ECONNREFUSED)
+                               goto ask_driver;
+                       leave(1, "connect");
+               }
+
+               do_connect(name, team, enter_status);
+               if (Send_message != NULL) {
+                       do_message();
+                       if (enter_status == Q_MESSAGE)
+                               break;
+                       Send_message = NULL;
+                       continue;
+               }
+               playit();
+               if ((enter_status = quit(enter_status)) == Q_QUIT)
+                       break;
+       }
+       leave(0, (char *) NULL);
+       /* NOTREACHED */
+       return(0);
+}
+
+/*
+ * Set Daemon to be the address of a hunt driver, or return 0 on failure.
+ *
+ * We start quietly probing for drivers. As soon as one driver is found
+ * we show it in the list. If we run out of drivers and we only have one
+ * then we choose it. Otherwise we present a list of the found drivers.
+ */
+static int
+find_driver()
+{
+       int last_driver, numdrivers, waiting, is_current;
+       struct driver *driver;
+       int c;
+       char buf[80];
+       const char *xname;
+
+       probe_drivers(Am_monitor ? C_MONITOR : C_PLAYER, Sock_host);
+
+       last_driver = -1;
+       numdrivers = 0;
+       waiting = 1;
+       for (;;) {
+               if (numdrivers == 0) {
+                       /* Silently wait for at least one driver */
+                       driver = next_driver();
+               } else if (!waiting || (driver = 
+                   next_driver_fd(STDIN_FILENO)) == (struct driver *)-1) {
+                       /* We have a key waiting, or no drivers left */
+                       c = getchar();
+                       if (c == '\r' || c == '\n' || c == ' ') {
+                               if (numdrivers == 1)
+                                       c = 'a';
+                               else if (last_driver != -1)
+                                       c = 'a' + last_driver;
+                       }
+                       if (c < 'a' || c >= numdrivers + 'a') {
+                               display_beep();
+                               continue;
+                       }
+                       driver = &drivers[c - 'a'];
+                       break;
+               }
+
+               if (driver == NULL) {
+                       waiting = 0;
+                       if (numdrivers == 0) {
+                               probe_cleanup();
+                               return 0;       /* Failure */
+                       }
+                       if (numdrivers == 1) {
+                               driver = &drivers[0];
+                               break;
+                       }
+                       continue;
+               }
+
+               /* Use the preferred host straight away. */
+               if (Sock_host)
+                       break;
+
+               if (numdrivers == 0) {
+                       display_clear_the_screen();
+                       display_move(1, 0);
+                       display_put_str("Pick one:");
+               }
+
+               /* Mark the last driver we used with an asterisk */
+               is_current = (last_driver == -1 && Daemon.sa_len != 0 && 
+                   memcmp(&Daemon, &driver->addr, Daemon.sa_len) == 0);
+               if (is_current)
+                       last_driver = numdrivers;
+
+               /* Display it in the list if there is room */
+               if (numdrivers < HEIGHT - 3) {
+                       xname = driver_name(driver);
+                       display_move(3 + numdrivers, 0);
+                       snprintf(buf, sizeof buf, "%6c %c    %s", 
+                           is_current ? '*' : ' ', 'a' + numdrivers, xname);
+                       display_put_str(buf);
+               }
+
+               /* Clear the last 'Enter letter' line if any */
+               display_move(4 + numdrivers, 0);
+               display_clear_eol();
+
+               if (last_driver != -1)
+                       snprintf(buf, sizeof buf, "Enter letter [%c]: ", 
+                           'a' + last_driver);
+               else
+                       snprintf(buf, sizeof buf, "Enter letter: ");
+
+               display_move(5 + numdrivers, 0);
+               display_put_str(buf);
+               display_refresh();
+
+               numdrivers++;
+       }
+
+       display_clear_the_screen();
+       Daemon = driver->addr;
+
+       probe_cleanup();
+       return 1;               /* Success */
+}
+
+static void
+dump_scores()
+{
+       struct  driver *driver;
+       int     s, cnt, i;
+       char    buf[1024];
+
+       probe_drivers(C_SCORES, Sock_host);
+       while ((driver = next_driver()) != NULL) {
+               printf("\n%s:\n", driver_name(driver));
+               fflush(stdout);
+
+               if ((s = socket(driver->addr.sa_family, SOCK_STREAM, 0)) < 0) {
+                       warn("socket");
+                       continue;
+               }
+               if (connect(s, &driver->addr, driver->addr.sa_len) < 0) {
+                       warn("connect");
+                       close(s);
+                       continue;
+               }
+               while ((cnt = read(s, buf, sizeof buf)) > 0) {
+                       /* Whittle out bad characters */
+                       for (i = 0; i < cnt; i++)
+                               if ((buf[i] < ' ' || buf[i] > '~') &&
+                                   buf[i] != '\n' && buf[i] != '\t')
+                                       buf[i] = '?';
+                       fwrite(buf, cnt, 1, stdout);
+               }
+               if (cnt < 0)
+                       warn("read");
+               (void)close(s);
+               if (Sock_host)
+                       break;
+       }
+       probe_cleanup();
+}
+
+
+/*
+ * bad_con:
+ *     We had a bad connection.  For the moment we assume that this
+ *     means the game is full.
+ */
+void
+bad_con()
+{
+       leave(1, "lost connection to huntd");
+}
+
+/*
+ * bad_ver:
+ *     version number mismatch.
+ */
+void
+bad_ver()
+{
+       errno = 0;
+       leave(1, "Version number mismatch. No go.");
+}
+
+/*
+ * sigterm:
+ *     Handle a terminate signal
+ */
+static void
+sigterm(int signo __unused)
+{
+       leave(0, (char *) NULL);
+}
+
+/*
+ * rmnl:
+ *     Remove a '\n' at the end of a string if there is one
+ */
+static void
+rmnl(char *s)
+{
+       char    *cp;
+
+       cp = strrchr(s, '\n');
+       if (cp != NULL)
+               *cp = '\0';
+}
+
+/*
+ * intr:
+ *     Handle a interrupt signal
+ */
+void
+intr(int dummy __unused)
+{
+       int     ch;
+       int     explained;
+       int     y, x;
+
+       (void) signal(SIGINT, SIG_IGN);
+       display_getyx(&y, &x);
+       display_move(HEIGHT, 0);
+       display_put_str("Really quit? ");
+       display_clear_eol();
+       display_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);
+                       display_move(y, x);
+                       display_refresh();
+                       return;
+               }
+               if (!explained) {
+                       display_put_str("(Yes or No) ");
+                       display_refresh();
+                       explained = TRUE;
+               }
+               display_beep();
+               display_refresh();
+       }
+}
+
+/*
+ * leave:
+ *     Leave the game somewhat gracefully, restoring all current
+ *     tty stats.
+ */
+static void
+leave(int eval, const char *mesg)
+{
+       int saved_errno;
+
+       saved_errno = errno;
+       if (in_visual) {
+               display_move(HEIGHT, 0);
+               display_refresh();
+               display_end();
+       }
+       errno = saved_errno;
+
+       if (errno == 0 && mesg != NULL)
+               errx(eval, mesg);
+       else if (mesg != NULL)
+               err(eval, mesg);
+       exit(eval);
+}
+
+/*
+ * env_init:
+ *     initialise game parameters from the HUNT envvar
+ */
+static long
+env_init(enter_status)
+       long    enter_status;
+{
+       int     i;
+       char    *envp, *envname, *s;
+
+       /* Map all keys to themselves: */
+       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';
+                                       strlcpy(name, envname, sizeof name);
+                                       break;
+                               }
+                               *s = '\0';
+                               strlcpy(name, envname, sizeof name);
+                               envp = s + 1;
+                       }
+                       else if (strncmp(envp, "port=", s - envp + 1) == 0) {
+                               use_port = s + 1;
+                               Server_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;
+                       }
+                       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)
+                               strlcpy(name, envp, sizeof name);
+                       else
+                               printf("unknown option %s\n", envp);
+               }
+       }
+       return enter_status;
+}
+
+/*
+ * fill_in_blanks:
+ *     quiz the user for the information they didn't provide earlier
+ */
+static void
+fill_in_blanks()
+{
+       int     i;
+       char    *cp;
+
+again:
+       if (name[0] != '\0') {
+               printf("Entering as '%s'", name);
+               if (team != ' ' && team != '-')
+                       printf(" on team %c.\n", team);
+               else
+                       putchar('\n');
+       } else {
+               printf("Enter your code name: ");
+               if (fgets(name, sizeof name, stdin) == NULL)
+                       exit(1);
+       }
+       rmnl(name);
+       if (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;
+               else if (i == '\n' || i == EOF || i == ' ')
+                       team = ' ';
+               /* ignore trailing chars */
+               while (i != '\n' && i != EOF)
+                       i = getchar();
+               if (team == '-') {
+                       printf("Teams must be numeric.\n");
+                       goto again;
+               }
+       }
+}
diff --git a/games/hunt/hunt/list.c b/games/hunt/hunt/list.c
new file mode 100644 (file)
index 0000000..6408bf5
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2001, David Leonard. All rights reserved.
+ * Redistribution and use in source and binary forms with or without
+ * modification are permitted provided that this notice is preserved.
+ * This software is provided ``as is'' without express or implied warranty.
+ *
+ * $OpenBSD: list.c,v 1.5 2007/09/04 22:39:31 hshoexer Exp $
+ * $DragonFly: src/games/hunt/hunt/list.c,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <arpa/inet.h>
+
+#include "hunt.h"
+#include "list.h"
+
+/* Wait at most 5 seconds for a reply */
+#define LIST_DELAY     5
+
+struct driver *drivers = NULL;
+int numdrivers = 0;
+int maxdrivers = 0;
+
+u_int16_t Server_port;
+
+static int numprobes = 0;
+static int probe_sock[64];
+static struct timeval probe_timeout;
+
+struct driver *
+next_driver()
+{
+
+       return next_driver_fd(-1);
+}
+
+struct driver *
+next_driver_fd(fd)
+       int     fd;
+{
+       fd_set  r;
+       int     maxfd = -1;
+       int     i, s, ret, len;
+       struct driver *driver;
+       u_int16_t resp;
+
+       if (fd == -1 && numprobes == 0)
+               return NULL;
+
+    again:
+       FD_ZERO(&r);
+       if (fd != -1) {
+               FD_SET(fd, &r);
+               maxfd = fd;
+       }
+       for (i = 0; i < numprobes; i++) {
+               FD_SET(probe_sock[i], &r);
+               if (probe_sock[i] > maxfd)
+                       maxfd = probe_sock[i];
+       }
+
+       probe_timeout.tv_sec = LIST_DELAY;
+       probe_timeout.tv_usec = 0;
+       ret = select(maxfd + 1, &r, NULL, NULL, &probe_timeout);
+
+       if (ret == -1) {
+               if (errno == EINTR)
+                       goto again;
+               err(1, "select");
+       }
+
+       if (ret == 0) {
+               /* Timeout - close all sockets */
+               for (i = 0; i < numprobes; i++)
+                       close(probe_sock[i]);
+               numprobes = 0;
+               return NULL;
+       }
+
+       if (fd != -1 && FD_ISSET(fd, &r)) 
+               /* Keypress. Return magic number */
+               return (struct driver *)-1;
+
+       for (i = 0; i < numprobes; i++)
+               /* Find the first ready socket */
+               if (FD_ISSET(probe_sock[i], &r))
+                       break;
+
+       s = probe_sock[i];
+
+       if (numdrivers >= maxdrivers) {
+               if (maxdrivers) {
+                       maxdrivers *= 2;
+                       drivers = realloc(drivers, sizeof *driver * maxdrivers);
+               } else {
+                       maxdrivers = 16;
+                       drivers = calloc(sizeof *driver, maxdrivers);
+               }
+               if (drivers == NULL)
+                       err(1, "malloc");
+       }
+       driver = &drivers[numdrivers];
+       len = sizeof driver->addr;
+       ret = recvfrom(s, &resp, sizeof resp, 0, &driver->addr, &len);
+       if (ret == -1)
+               goto again;
+       driver->response = ntohs(resp);
+
+       switch (driver->addr.sa_family) {
+       case AF_INET:
+       case AF_INET6:
+               ((struct sockaddr_in *)&driver->addr)->sin_port =
+                   htons(driver->response);
+               break;
+       }
+       numdrivers++;
+       return driver;
+}
+
+/* Return the hostname for a driver. */
+const char *
+driver_name(driver)
+       struct driver *driver;
+{
+       const char *name;
+       static char buf[80];
+       struct hostent *hp;
+       struct sockaddr_in *sin;
+
+       name = NULL;
+
+       if (driver->addr.sa_family == AF_INET) {
+               sin = (struct sockaddr_in *)&driver->addr;
+               hp = gethostbyaddr((char *)&sin->sin_addr, 
+                   sizeof sin->sin_addr, AF_INET);
+               if (hp != NULL)
+                       name = hp->h_name;
+               else {
+                       name = inet_ntop(AF_INET, &sin->sin_addr, 
+                           buf, sizeof buf);
+               }
+       }
+
+       return name;
+}
+
+static int
+start_probe(struct sockaddr *addr, u_int16_t req)
+{
+       u_int16_t msg;
+       int s;
+       int enable;
+
+       if (numprobes >= (int)(sizeof probe_sock / sizeof probe_sock[0])) {
+               /* Just ridiculous */
+               return -1;
+       }
+
+       s = socket(addr->sa_family, SOCK_DGRAM, 0);
+       if (s < 0) {
+               warn("socket");
+               return -1;
+       }
+
+       enable = 1;
+       setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof enable);
+
+       switch (addr->sa_family) {
+       case AF_INET:
+       case AF_INET6:
+               ((struct sockaddr_in *)addr)->sin_port = 
+                   htons(Server_port);
+               break;
+       }
+
+       msg = htons(req);
+       if (sendto(s, &msg, sizeof msg, 0, addr, addr->sa_len) == -1)
+               warn("sendto");
+       probe_sock[numprobes++] = s;
+
+       return 0;
+}
+
+void
+probe_cleanup()
+{
+       int i;
+
+       for (i = 0; i < numprobes; i++)
+               close(probe_sock[i]);
+       numprobes = 0;
+}
+
+/*
+ * If we have no preferred host then send a broadcast message to everyone.
+ * Otherwise, send the request message only to the preferred host.
+ */
+void
+probe_drivers(req, preferred)
+       u_int16_t       req;
+       char            *preferred;
+{
+       struct sockaddr_in *target;
+       struct sockaddr_in localhost;
+       struct hostent *he;
+        char *inbuf = NULL, *ninbuf;
+        struct ifconf ifc;
+        struct ifreq *ifr;
+        int fd, inlen = 8192;
+        int i, len;
+
+       numdrivers = 0;
+
+       probe_cleanup();
+
+       /* Send exclusively to a preferred host. */
+       if (preferred) {
+               struct sockaddr_in sin;
+
+               target = NULL;
+
+               if (!target) {
+                       sin.sin_family = AF_INET;
+                       sin.sin_len = sizeof sin;
+                       if (inet_pton(AF_INET, preferred, &sin.sin_addr) == 1)
+                               target = &sin;
+               }
+
+               if (!target && (he = gethostbyname(preferred)) != NULL) {
+                       sin.sin_family = he->h_addrtype;
+                       sin.sin_len = sizeof sin;
+                       memcpy(&sin.sin_addr, he->h_addr, he->h_length);
+                       target = &sin;
+               }
+
+               if (!target)
+                       errx(1, "Bad hostname: %s", preferred);
+
+               start_probe((struct sockaddr *)target, req);
+               return;
+       }
+
+       /* Send a query to the local machine: */
+       localhost.sin_family = AF_INET;
+       localhost.sin_len = sizeof localhost;
+       localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       start_probe((struct sockaddr *)&localhost, req);
+
+        if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+                err(1, "socket");
+
+       /* Find all attached networks: */
+        while (1) {
+                ifc.ifc_len = inlen;
+                if ((ninbuf = realloc(inbuf, inlen)) == NULL)
+                       err(1, "malloc");
+                ifc.ifc_buf = inbuf = ninbuf;
+                if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) 
+                        err(1, "SIOCGIFCONF");
+                if (ifc.ifc_len + (int)sizeof(*ifr) < inlen)
+                        break;
+                inlen *= 2;
+        }
+
+       /* Send a request to every attached broadcast address: */
+        ifr = ifc.ifc_req;
+        for (i = 0; i < ifc.ifc_len;
+             i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) {
+                len = sizeof(ifr->ifr_name) +
+                      (ifr->ifr_addr.sa_len > sizeof(struct sockaddr) ?
+                       ifr->ifr_addr.sa_len : sizeof(struct sockaddr));
+
+               if (ifr->ifr_addr.sa_family != AF_INET)
+                       continue;
+
+                if (ioctl(fd, SIOCGIFFLAGS, (caddr_t)ifr) < 0) {
+                        warn("%s: SIOCGIFFLAGS", ifr->ifr_name);
+                       continue;
+               }
+                if ((ifr->ifr_flags & IFF_UP) == 0)
+                       continue;
+               if ((ifr->ifr_flags & IFF_BROADCAST) != 0) {
+                       if (ioctl(fd, SIOCGIFBRDADDR, (caddr_t)ifr) < 0)  {
+                               warn("%s: SIOCGIFBRDADDR", ifr->ifr_name);
+                               continue;
+                       }
+                       target = (struct sockaddr_in *)&ifr->ifr_dstaddr;
+               } else if ((ifr->ifr_flags & IFF_POINTOPOINT) != 0) {
+                       if (ioctl(fd, SIOCGIFDSTADDR, (caddr_t)ifr) < 0)  {
+                               warn("%s: SIOCGIFDSTADDR", ifr->ifr_name);
+                               continue;
+                       }
+                       target = (struct sockaddr_in *)&ifr->ifr_broadaddr;
+               } else
+                       continue;
+
+               start_probe((struct sockaddr *)target, req);
+        }
+        free(inbuf);
+        (void) close(fd);
+}
diff --git a/games/hunt/hunt/list.h b/games/hunt/hunt/list.h
new file mode 100644 (file)
index 0000000..4d6546c
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * David Leonard <d@openbsd.org>, 1999.  Public domain.
+ *
+ * $OpenBSD: list.h,v 1.3 2003/06/17 00:36:36 pjanzen Exp $
+ * $DragonFly: src/games/hunt/hunt/list.h,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+struct driver {
+       struct sockaddr addr;
+       u_int16_t       response;
+       int             once;
+};
+
+extern struct driver *drivers;
+extern u_int16_t Server_port;
+
+struct  driver *next_driver(void);
+struct  driver *next_driver_fd(int);
+const char *   driver_name(struct driver *);
+void   probe_drivers(u_int16_t, char *);
+void   probe_cleanup(void);
diff --git a/games/hunt/hunt/otto.c b/games/hunt/hunt/otto.c
new file mode 100644 (file)
index 0000000..faaf96e
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: otto.c,v 1.9 2006/03/27 00:10:15 tedu Exp $
+ * $NetBSD: otto.c,v 1.2 1997/10/10 16:32:39 lukem Exp $
+ * $DragonFly: src/games/hunt/hunt/otto.c,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+/*
+ *     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.
+ */
+
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "hunt.h"
+#include "client.h"
+#include "display.h"
+
+#include <stdio.h>
+#define panic(m)       _panic(__FILE__,__LINE__,m)
+
+useconds_t     Otto_pause      = 55000;
+
+int    Otto_mode;
+
+# undef                WALL
+# undef                NORTH
+# undef                SOUTH
+# undef                WEST
+# undef                EAST
+# undef                FRONT
+# undef                LEFT
+# undef                BACK
+# undef                RIGHT
+
+# define       SCREEN(y, x)    display_atyx(y, x)
+
+# define       OPPONENT        "{}i!"
+# define       PROPONENT       "^v<>"
+# define       WALL            "+\\/#*-|"
+# define       PUSHOVER        " bg;*#&"
+# define       SHOTS           "$@Oo:"
+
+/* number of "directions" */
+# define       NUMDIRECTIONS   4
+# define       direction(abs,rel)      (((abs) + (rel)) % NUMDIRECTIONS)
+
+/* 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[1024];  /* XXX */
+static int     comlen;
+
+# 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 void            attack(int, struct item *);
+static void            duck(int);
+static void            face_and_move_direction(int, int);
+static int             go_for_ammo(char);
+static void            ottolook(int, struct item *);
+static void            look_around(void);
+static int             stop_look(struct item *, char, int, int);
+static void            wander(void);
+static void            _panic(const char *, int, const char *);
+
+int
+otto(y, x, face, buf, buflen)
+       int     y, x;
+       char    face;
+       char    *buf;
+       size_t  buflen;
+{
+       int             i;
+
+       if (usleep(Otto_pause) < 0)
+               panic("usleep");
+
+       /* 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:        panic("unknown face");
+       }
+       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);
+       } 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);
+       } 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:
+       if (comlen) {
+               if (comlen > (int)buflen)
+                       panic("not enough buffer space");
+               memcpy(buf, command, comlen);
+       }
+       return comlen;
+}
+
+static int
+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:
+       case BOOT:
+       case BOOT_PAIR:
+               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:
+       case SLIME:
+               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;
+       }
+}
+
+static void
+ottolook(rel_dir, itemp)
+       int             rel_dir;
+       struct  item    *itemp;
+{
+       int             r, c;
+       char            ch;
+
+       r = 0;
+       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;
+                       if (r >= 0)
+                               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;
+                       if (r < HEIGHT)
+                               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_west;
+                               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:
+               panic("unknown look");
+       }
+}
+
+static void
+look_around()
+{
+       int     i;
+
+       for (i = 0; i < NUMDIRECTIONS; i++) {
+               ottolook(i, &flbr[i]);
+       }
+}
+
+/*
+ *     as a side effect modifies facing and location (row, col)
+ */
+
+static void
+face_and_move_direction(rel_dir, distance)
+       int     rel_dir, distance;
+{
+       int     old_facing;
+       char    cmd;
+
+       old_facing = facing;
+       cmd = DIRKEYS[facing = direction(facing, rel_dir)];
+
+       if (rel_dir != FRONT) {
+               int     i;
+               struct  item    items[NUMDIRECTIONS];
+
+               command[comlen++] = toupper(cmd);
+               if (distance == 0) {
+                       /* rotate ottolook'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 void
+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 void
+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 int
+go_for_ammo(mine)
+       char    mine;
+{
+       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 void
+wander()
+{
+       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;
+               dir_count = 1;
+               break;
+       }
+       if (dir_count == 0) {
+               duck(random() % NUMDIRECTIONS);
+               num_turns = 0;
+               return;
+       } else {
+               rel_dir = ffs(dir_mask) - 1;
+       }
+       if (rel_dir == FRONT)
+               num_turns++;
+       else
+               num_turns = 0;
+
+       face_and_move_direction(rel_dir, 1);
+}
+
+/* Otto always re-enters the game, cloaked. */
+int
+otto_quit(int old_status __unused)
+{
+       return Q_CLOAK;
+}
+
+static void
+_panic(file, line, msg)
+       const char *file;
+       int line;
+       const char *msg;
+{
+
+       fprintf(stderr, "%s:%d: panic! %s\n", file, line, msg);
+       abort();
+}
diff --git a/games/hunt/hunt/playit.c b/games/hunt/hunt/playit.c
new file mode 100644 (file)
index 0000000..df9fa5f
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: playit.c,v 1.8 2003/06/11 08:45:25 pjanzen Exp $
+ * $NetBSD: playit.c,v 1.4 1997/10/20 00:37:15 lukem Exp $
+ * $DragonFly: src/games/hunt/hunt/playit.c,v 1.1 2008/09/02 21:50:20 dillon Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <ctype.h>
+#include <termios.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "hunt.h"
+#include "display.h"
+#include "client.h"
+
+static int     nchar_send;
+static FLAG    Last_player;
+static int     Otto_expect;
+
+# define       MAX_SEND        5
+
+/*
+ * 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;
+
+#define        GETCHR()        (--icnt < 0 ? getchr() : *iptr++)
+
+static unsigned char   getchr(void);
+static void            send_stuff(void);
+
+/*
+ * playit:
+ *     Play a given game, handling all the curses commands from
+ *     the driver.
+ */
+void
+playit()
+{
+       int             ch;
+       int             y, x;
+       u_int32_t       version;
+       int             otto_y, otto_x;
+       char            otto_face = ' ';
+       int             chars_processed;
+
+       if (read(Socket, &version, sizeof version) != sizeof version) {
+               bad_con();
+               /* NOTREACHED */
+       }
+       if (ntohl(version) != (unsigned int)HUNT_VERSION) {
+               bad_ver();
+               /* NOTREACHED */
+       }
+       errno = 0;
+       nchar_send = MAX_SEND;
+       Otto_expect = 0;
+       while ((ch = GETCHR()) != EOF) {
+               switch (ch & 0377) {
+                 case MOVE:
+                       y = GETCHR();
+                       x = GETCHR();
+                       display_move(y, x);
+                       break;
+
+                 case CLRTOEOL:
+                       display_clear_eol();
+                       break;
+                 case CLEAR:
+                       display_clear_the_screen();
+                       break;
+                 case REFRESH:
+                       display_refresh();
+                       break;
+                 case REDRAW:
+                       display_redraw_screen();
+                       display_refresh();
+                       break;
+                 case ENDWIN:
+                       display_refresh();
+                       if ((ch = GETCHR()) == LAST_PLAYER)
+                               Last_player = TRUE;
+                       ch = EOF;
+                       goto out;
+                 case BELL:
+                       display_beep();
+                       break;
+                 case READY:
+                       chars_processed = GETCHR();
+                       display_refresh();
+                       if (nchar_send < 0)
+                               tcflush(STDIN_FILENO, TCIFLUSH);
+                       nchar_send = MAX_SEND;
+                       if (Otto_mode) {
+                               /*
+                                * The driver returns the number of keypresses
+                                * that it has processed. Use this to figure
+                                * out if otto's commands have completed.
+                                */
+                               Otto_expect -= chars_processed;
+                               if (Otto_expect == 0) {
+                                       /* not very fair! */
+                                       static char buf[MAX_SEND * 2];
+                                       int len;
+
+                                       /* Ask otto what it wants to do: */
+                                       len = otto(otto_y, otto_x, otto_face,
+                                               buf, sizeof buf);
+                                       if (len) {
+                                               /* Pass it on to the driver: */
+                                               write(Socket, buf, len);
+                                               /* Update expectations: */
+                                               Otto_expect += len;
+                                       }
+                               }
+                       }
+                       break;
+                 case ADDCH:
+                       ch = GETCHR();
+                       /* FALLTHROUGH */
+                 default:
+                       if (!isprint(ch))
+                               ch = ' ';
+                       display_put_ch(ch);
+                       if (Otto_mode)
+                               switch (ch) {
+                               case '<':
+                               case '>':
+                               case '^':
+                               case 'v':
+                                       otto_face = ch;
+                                       display_getyx(&otto_y, &otto_x);
+                                       otto_x--;
+                                       break;
+                               }
+                       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()
+{
+       fd_set  readfds, s_readfds;
+       int     nfds, s_nfds;
+
+       FD_ZERO(&s_readfds);
+       FD_SET(Socket, &s_readfds);
+       FD_SET(STDIN_FILENO, &s_readfds);
+       s_nfds = (Socket > STDIN_FILENO) ? Socket : STDIN_FILENO;
+       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 (FD_ISSET(STDIN_FILENO, &readfds))
+               send_stuff();
+       if (!FD_ISSET(Socket, &readfds))
+               goto one_more_time;
+       icnt = read(Socket, ibuf, sizeof ibuf);
+       if (icnt <= 0) {
+               bad_con();
+               /* NOTREACHED */
+       }
+       iptr = ibuf;
+       icnt--;
+       return *iptr++;
+}
+
+/*
+ * send_stuff:
+ *     Send standard input characters to the driver
+ */
+static void
+send_stuff()
+{
+       int             count;
+       char            *sp, *nsp;
+       static char     inp[BUFSIZ];
+       static char     Buf[BUFSIZ];
+
+       /* Drain the user's keystrokes: */
+       count = read(STDIN_FILENO, Buf, sizeof Buf);
+       if (count < 0)
+               err(1, "read");
+       if (count == 0)
+               return;
+
+       if (nchar_send <= 0 && !no_beep) {
+               display_beep();
+               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';
+       for (sp = Buf, nsp = inp; *sp != '\0'; sp++, nsp++) {
+               *nsp = map_key[(int)*sp];
+               if (*nsp == 'q')
+                       intr(0);
+       }
+       count = nsp - inp;
+       if (count) {
+               nchar_send -= count;
+               if (nchar_send < 0)
+                       count += nchar_send;
+               (void) write(Socket, inp, count);
+               if (Otto_mode) {
+                       /*
+                        * The user can insert commands over otto.
+                        * So, otto shouldn't be alarmed when the 
+                        * server processes more than otto asks for.
+                        */
+                       Otto_expect += count;
+               }
+       }
+}
+
+/*
+ * quit:
+ *     Handle the end of the game when the player dies
+ */
+int
+quit(old_status)
+       int     old_status;
+{
+       int     explain, ch;
+
+       if (Last_player)
+               return Q_QUIT;
+       if (Otto_mode)
+               return otto_quit(old_status);
+       display_move(HEIGHT, 0);
+       display_put_str("Re-enter game [ynwo]? ");
+       display_clear_eol();
+       explain = FALSE;
+       for (;;) {
+               display_refresh();
+               if (isupper(ch = getchar()))
+                       ch = tolower(ch);
+               if (ch == 'y')
+                       return old_status;
+               else if (ch == 'o')
+                       break;
+               else if (ch == 'n') {
+                       display_move(HEIGHT, 0);
+                       display_put_str("Write a parting message [yn]? ");
+                       display_clear_eol();
+                       display_refresh();
+                       for (;;) {
+                               if (isupper(ch = getchar()))
+                                       ch = tolower(ch);
+                               if (ch == 'y')
+                                       goto get_message;
+                               if (ch == 'n')
+                                       return Q_QUIT;
+                       }
+               }
+               else if (ch == 'w') {
+                       static  char    buf[WIDTH + WIDTH % 2];
+                       char            *cp, c;
+
+get_message:
+                       c = ch;         /* save how we got here */
+                       display_move(HEIGHT, 0);
+                       display_put_str("Message: ");
+                       display_clear_eol();
+                       display_refresh();
+                       cp = buf;
+                       for (;;) {
+                               display_refresh();
+                               if ((ch = getchar()) == '\n' || ch == '\r')
+                                       break;
+                               if (display_iserasechar(ch))
+                               {
+                                       if (cp > buf) {
+                                               int y, x;
+
+                                               display_getyx(&y, &x);
+                                               display_move(y, x - 1);
+                                               cp -= 1;
+                                               display_clear_eol();
+                                       }
+                                       continue;
+                               }
+                               else if (display_iskillchar(ch))
+                               {
+                                       int y, x;
+
+                                       display_getyx(&y, &x);
+                                       display_move(y, x - (cp - buf));
+                                       cp = buf;
+                                       display_clear_eol();
+                                       continue;
+                               } else if (!isprint(ch)) {
+                                       display_beep();
+                                       continue;
+                               }
+                               display_put_ch(ch);
+                               *cp++ = ch;
+                               if (cp + 1 >= buf + sizeof buf)
+                                       break;
+                       }
+                       *cp = '\0';
+                       Send_message = buf;
+                       return (c == 'w') ? old_status : Q_MESSAGE;
+               }
+               display_beep();
+               if (!explain) {
+                       display_put_str("(Yes, No, Write message, or Options) ");
+                       explain = TRUE;
+               }
+       }
+
+       display_move(HEIGHT, 0);
+       display_put_str("Scan, Cloak, Flying, or Quit? ");
+       display_clear_eol();
+       display_refresh();
+       explain = FALSE;
+       for (;;) {
+               if (isupper(ch = getchar()))
+                       ch = tolower(ch);
+               if (ch == 's')
+                       return Q_SCAN;
+               else if (ch == 'c')
+                       return Q_CLOAK;
+               else if (ch == 'f')
+                       return Q_FLY;
+               else if (ch == 'q')
+                       return Q_QUIT;
+               display_beep();
+               if (!explain) {
+                       display_put_str("[SCFQ] ");
+                       explain = TRUE;
+               }
+               display_refresh();
+       }
+}
+
+/*
+ * do_message:
+ *     Send a message to the driver and return
+ */
+void
+do_message()
+{
+       u_int32_t       version;
+
+       if (read(Socket, &version, sizeof version) != sizeof version) {
+               bad_con();
+               /* NOTREACHED */
+       }
+       if (ntohl(version) != (unsigned int)HUNT_VERSION) {
+               bad_ver();
+               /* NOTREACHED */
+       }
+       if (write(Socket, Send_message, strlen(Send_message)) < 0) {
+               bad_con();
+               /* NOTREACHED */
+       }
+       (void) close(Socket);
+}
diff --git a/games/hunt/huntd/Makefile b/games/hunt/huntd/Makefile
new file mode 100644 (file)
index 0000000..b335e58
--- /dev/null
@@ -0,0 +1,13 @@
+#      $NetBSD: Makefile,v 1.1 1997/10/04 09:11:21 mrg Exp $
+#      $OpenBSD: Makefile,v 1.4 1999/03/14 02:07:30 pjanzen Exp $
+#      $DragonFly: src/games/hunt/huntd/Makefile,v 1.1 2008/09/02 21:50:21 dillon Exp $
+
+PROG=  huntd
+SRCS=  answer.c conf.c draw.c driver.c execute.c expl.c \
+       extern.c makemaze.c shots.c terminal.c
+MAN=   huntd.6
+LDADD+=        -lwrap
+DPADD+= ${LIBWRAP}
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/games/hunt/huntd/answer.c b/games/hunt/huntd/answer.c
new file mode 100644 (file)
index 0000000..3c66f69
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: answer.c,v 1.11 2007/11/06 10:22:29 chl Exp $
+ * $NetBSD: answer.c,v 1.3 1997/10/10 16:32:50 lukem Exp $
+ * $DragonFly: src/games/hunt/huntd/answer.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <tcpd.h>
+
+#include "hunt.h"
+#include "server.h"
+#include "conf.h"
+
+/* Exported symbols for hosts_access(): */
+int allow_severity     = LOG_INFO;
+int deny_severity      = LOG_WARNING;
+
+
+/* List of spawning connections: */
+struct spawn           *Spawn = NULL;
+
+static void    stplayer(PLAYER *, int);
+static void    stmonitor(PLAYER *);
+static IDENT * get_ident(struct sockaddr *, int, uid_t, char *, char);
+
+void
+answer_first()
+{
+       struct sockaddr         sockstruct;
+       int                     newsock;
+       socklen_t               socklen;
+       int                     flags;
+       struct request_info     ri;
+       struct spawn *sp;
+
+       /* Answer the call to hunt: */
+       socklen = sizeof sockstruct;
+       newsock = accept(Socket, (struct sockaddr *) &sockstruct, &socklen);
+       if (newsock < 0) {
+               logit(LOG_ERR, "accept");
+               return;
+       }
+
+       /* Check for access permissions: */
+       request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, newsock, 0);
+       fromhost(&ri);
+       if (hosts_access(&ri) == 0) {
+               logx(LOG_INFO, "rejected connection from %s", eval_client(&ri));
+               close(newsock);
+               return;
+       }
+
+       /* Remember this spawning connection: */
+       sp = (struct spawn *)malloc(sizeof *sp);
+       if (sp == NULL) {
+               logit(LOG_ERR, "malloc");
+               close(newsock);
+               return;
+       }
+       memset(sp, '\0', sizeof *sp);
+
+       /* Keep the calling machine's source addr for ident purposes: */
+       memcpy(&sp->source, &sockstruct, sizeof sp->source);
+       sp->sourcelen = socklen;
+
+       /* Warn if we lose connection info: */
+       if (socklen > sizeof Spawn->source) 
+               logx(LOG_WARNING, 
+                   "struct sockaddr is not big enough! (%d > %zu)",
+                   socklen, sizeof Spawn->source);
+
+       /*
+        * 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);
+
+       /* Start listening to the spawning connection */
+       sp->fd = newsock;
+       FD_SET(sp->fd, &Fds_mask);
+       if (sp->fd >= Num_fds)
+               Num_fds = sp->fd + 1;
+
+       sp->reading_msg = 0;
+       sp->inlen = 0;
+
+       /* Add to the spawning list */
+       if ((sp->next = Spawn) != NULL)
+               Spawn->prevnext = &sp->next;
+       sp->prevnext = &Spawn;
+       Spawn = sp;
+}
+
+int
+answer_next(sp)
+       struct spawn *sp;
+{
+       PLAYER                  *pp;
+       char                    *cp1, *cp2;
+       u_int32_t               version;
+       FILE                    *conn;
+       int                     len;
+       char                    teamstr[] = "[x]";
+
+       if (sp->reading_msg) {
+               /* Receive a message from a player */
+               len = read(sp->fd, sp->msg + sp->msglen, 
+                   sizeof sp->msg - sp->msglen);
+               if (len < 0)
+                       goto error;
+               sp->msglen += len;
+               if (len && sp->msglen < (int)(sizeof sp->msg))
+                       return FALSE;
+
+               teamstr[1] = sp->team;
+               outyx(ALL_PLAYERS, HEIGHT, 0, "%s%s: %.*s",
+                       sp->name,
+                       sp->team == ' ' ? "": teamstr,
+                       sp->msglen,
+                       sp->msg);
+               ce(ALL_PLAYERS);
+               sendcom(ALL_PLAYERS, REFRESH);
+               sendcom(ALL_PLAYERS, READY, 0);
+               flush(ALL_PLAYERS);
+               goto close_it;
+       }
+
+       /* Fill the buffer */
+       len = read(sp->fd, sp->inbuf + sp->inlen, 
+           sizeof sp->inbuf - sp->inlen);
+       if (len <= 0)
+               goto error;
+       sp->inlen += len;
+       if (sp->inlen < (int)(sizeof sp->inbuf))
+               return FALSE;
+
+       /* Extract values from the buffer */
+       cp1 = sp->inbuf;
+       memcpy(&sp->uid, cp1, sizeof (u_int32_t));
+       cp1+= sizeof(u_int32_t);
+       memcpy(sp->name, cp1, NAMELEN);
+       cp1+= NAMELEN;
+       memcpy(&sp->team, cp1, sizeof (u_int8_t));
+       cp1+= sizeof(u_int8_t);
+       memcpy(&sp->enter_status, cp1, sizeof (u_int32_t));
+       cp1+= sizeof(u_int32_t);
+       memcpy(sp->ttyname, cp1, NAMELEN);
+       cp1+= NAMELEN;
+       memcpy(&sp->mode, cp1, sizeof (u_int32_t));
+       cp1+= sizeof(u_int32_t);
+
+       /* Convert data from network byte order: */
+       sp->uid = ntohl(sp->uid);
+       sp->enter_status = ntohl(sp->enter_status);
+       sp->mode = ntohl(sp->mode);
+
+       /*
+        * Make sure the name contains only printable characters
+        * since we use control characters for cursor control
+        * between driver and player processes
+        */
+       sp->name[NAMELEN] = '\0';
+       for (cp1 = cp2 = sp->name; *cp1 != '\0'; cp1++)
+               if (isprint(*cp1) || *cp1 == ' ')
+                       *cp2++ = *cp1;
+       *cp2 = '\0';
+
+       /* Make sure team name is valid */
+       if (sp->team < '1' || sp->team > '9')
+               sp->team = ' ';
+
+       /* Tell the other end this server's hunt driver version: */
+       version = htonl((u_int32_t) HUNT_VERSION);
+       (void) write(sp->fd, &version, sizeof version);
+
+       if (sp->mode == C_MESSAGE) {
+               /* The clients only wants to send a message: */
+               sp->msglen = 0;
+               sp->reading_msg = 1;
+               return FALSE;
+       }
+
+       /* Use a stdio file descriptor from now on: */
+       conn = fdopen(sp->fd, "w");
+
+       /* The player is a monitor: */
+       if (sp->mode == C_MONITOR) {
+               if (conf_monitor && End_monitor < &Monitor[MAXMON]) {
+                       pp = End_monitor++;
+                       if (sp->team == ' ')
+                               sp->team = '*';
+               } else {
+                       /* Too many monitors */
+                       fprintf(conn, "Too many monitors\n");
+                       fflush(conn);
+                       logx(LOG_NOTICE, "too many monitors");
+                       goto close_it;
+               }
+
+       /* The player is a normal hunter: */
+       } else {
+               if (End_player < &Player[MAXPL])
+                       pp = End_player++;
+               else {
+                       fprintf(conn, "Too many players\n");
+                       fflush(conn);
+                       /* Too many players */
+                       logx(LOG_NOTICE, "too many players");
+                       goto close_it;
+               }
+       }
+
+       /* Find the player's running scorecard */
+       pp->p_ident = get_ident(&sp->source, sp->sourcelen, sp->uid, 
+           sp->name, sp->team);
+       pp->p_output = conn;
+       pp->p_death[0] = '\0';
+       pp->p_fd = sp->fd;
+
+       /* No idea where the player starts: */
+       pp->p_y = 0;
+       pp->p_x = 0;
+
+       /* Mode-specific initialisation: */
+       if (sp->mode == C_MONITOR)
+               stmonitor(pp);
+       else
+               stplayer(pp, sp->enter_status);
+
+       /* And, they're off! Caller should remove and free sp. */
+       return TRUE;
+
+error:
+       if (len < 0) 
+               logit(LOG_WARNING, "read");
+       else
+               logx(LOG_WARNING, "lost connection to new client");
+
+close_it:
+       /* Destroy the spawn */
+       *sp->prevnext = sp->next;
+       if (sp->next) sp->next->prevnext = sp->prevnext;
+       FD_CLR(sp->fd, &Fds_mask);
+       close(sp->fd);
+       free(sp);
+       return FALSE;
+}
+
+/* Start a monitor: */
+static void
+stmonitor(pp)
+       PLAYER  *pp;
+{
+
+       /* Monitors get to see the entire maze: */
+       memcpy(pp->p_maze, Maze, sizeof pp->p_maze);
+       drawmaze(pp);
+
+       /* Put the monitor's name near the bottom right on all screens: */
+       outyx(ALL_PLAYERS, 
+               STAT_MON_ROW + 1 + (pp - Monitor), STAT_NAME_COL,
+               "%5.5s%c%-10.10s %c", " ", 
+               stat_char(pp), pp->p_ident->i_name, pp->p_ident->i_team);
+
+       /* Ready the monitor: */
+       sendcom(pp, REFRESH);
+       sendcom(pp, READY, 0);
+       flush(pp);
+}
+
+/* Start a player: */
+static void
+stplayer(newpp, enter_status)
+       PLAYER  *newpp;
+       int     enter_status;
+{
+       int     x, y;
+       PLAYER  *pp;
+       int len;
+
+       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];
+
+       /* Drop the new player somewhere in the maze: */
+       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;
+
+       /* Send them flying if needed */
+       if (enter_status == Q_FLY && conf_fly) {
+               newpp->p_flying = rand_num(conf_flytime);
+               newpp->p_flyx = 2 * rand_num(conf_flystep + 1) - conf_flystep;
+               newpp->p_flyy = 2 * rand_num(conf_flystep + 1) - conf_flystep;
+               newpp->p_face = FLYER;
+       } else {
+               newpp->p_flying = -1;
+               newpp->p_face = rand_dir();
+       }
+
+       /* Initialize the new player's attributes: */
+       newpp->p_damage = 0;
+       newpp->p_damcap = conf_maxdam;
+       newpp->p_nchar = 0;
+       newpp->p_ncount = 0;
+       newpp->p_nexec = 0;
+       newpp->p_ammo = conf_ishots;
+       newpp->p_nboots = 0;
+
+       /* Decide on what cloak/scan status to enter with */
+       if (enter_status == Q_SCAN && conf_scan) {
+               newpp->p_scan = conf_scanlen * Nplayer;
+               newpp->p_cloak = 0;
+       } else if (conf_cloak) {
+               newpp->p_scan = 0;
+               newpp->p_cloak = conf_cloaklen;
+       } else {
+               newpp->p_scan = 0;
+               newpp->p_cloak = 0;
+       }
+       newpp->p_ncshot = 0;
+
+       /*
+        * For each new player, place a large mine and
+        * a small mine somewhere in the maze:
+        */
+       do {
+               x = rand_num(WIDTH - 1) + 1;
+               y = rand_num(HEIGHT - 1) + 1;
+       } while (Maze[y][x] != SPACE);
+       Maze[y][x] = GMINE;
+       for (pp = Monitor; pp < End_monitor; pp++)
+               check(pp, y, x);
+
+       do {
+               x = rand_num(WIDTH - 1) + 1;
+               y = rand_num(HEIGHT - 1) + 1;
+       } while (Maze[y][x] != SPACE);
+       Maze[y][x] = MINE;
+       for (pp = Monitor; pp < End_monitor; pp++)
+               check(pp, y, x);
+
+       /* Create a score line for the new player: */
+       (void) snprintf(Buf, sizeof 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);
+       len = strlen(Buf);
+       y = STAT_PLAY_ROW + 1 + (newpp - Player);
+       for (pp = Player; pp < End_player; pp++) {
+               if (pp != newpp) {
+                       /* Give everyone a few more shots: */
+                       pp->p_ammo += conf_nshots;
+                       newpp->p_ammo += conf_nshots;
+                       outyx(pp, y, STAT_NAME_COL, Buf, len);
+                       ammo_update(pp);
+               }
+       }
+       for (pp = Monitor; pp < End_monitor; pp++)
+               outyx(pp, y, STAT_NAME_COL, Buf, len);
+
+       /* Show the new player what they can see and where they are: */
+       drawmaze(newpp);
+       drawplayer(newpp, TRUE);
+       look(newpp);
+
+       /* Make sure that the position they enter in will be erased: */
+       if (enter_status == Q_FLY && conf_fly)
+               showexpl(newpp->p_y, newpp->p_x, FLYER);
+
+       /* Ready the new player: */
+       sendcom(newpp, REFRESH);
+       sendcom(newpp, READY, 0);
+       flush(newpp);
+}
+
+/*
+ * rand_dir:
+ *     Return a random direction
+ */
+int
+rand_dir()
+{
+       switch (rand_num(4)) {
+         case 0:
+               return LEFTS;
+         case 1:
+               return RIGHT;
+         case 2:
+               return BELOW;
+         case 3:
+               return ABOVE;
+       }
+       /* NOTREACHED */
+       return(-1);
+}
+
+/*
+ * get_ident:
+ *     Get the score structure of a player
+ */
+static IDENT *
+get_ident(sa, salen, uid, name, team)
+       struct sockaddr *sa;
+       int     salen __unused;
+       uid_t   uid;
+       char    *name;
+       char    team;
+{
+       IDENT           *ip;
+       static IDENT    punt;
+       u_int32_t       machine;
+
+       if (sa->sa_family == AF_INET)
+               machine = ntohl((u_long)((struct sockaddr_in *)sa)->sin_addr.s_addr);
+       else
+               machine = 0;
+
+       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_team != team) {
+                       logx(LOG_INFO, "player %s %s team %c",
+                               name,
+                               team == ' ' ? "left" : ip->i_team == ' ' ? 
+                                       "joined" : "changed to",
+                               team == ' ' ? ip->i_team : team);
+                       ip->i_team = team;
+               }
+               if (ip->i_entries < conf_scoredecay)
+                       ip->i_entries++;
+               else
+                       ip->i_kills = (ip->i_kills * (conf_scoredecay - 1))
+                               / conf_scoredecay;
+               ip->i_score = ip->i_kills / (double) ip->i_entries;
+       }
+       else {
+               /* Alloc new entry -- it is released in clear_scores() */
+               ip = (IDENT *) malloc(sizeof (IDENT));
+               if (ip == NULL) {
+                       logit(LOG_ERR, "malloc");
+                       /* Fourth down, time to punt */
+                       ip = &punt;
+               }
+               ip->i_machine = machine;
+               ip->i_team = team;
+               ip->i_uid = uid;
+               strlcpy(ip->i_name, name, sizeof ip->i_name);
+               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;
+
+               logx(LOG_INFO, "new player: %s%s%c%s",
+                       name, 
+                       team == ' ' ? "" : " (team ",
+                       team,
+                       team == ' ' ? "" : ")");
+       }
+
+       return ip;
+}
+
+void
+answer_info(fp)
+       FILE *fp;
+{
+       struct spawn *sp;
+       char buf[128];
+       const char *bf;
+       struct sockaddr_in *sa;
+
+       if (Spawn == NULL)
+               return;
+       fprintf(fp, "\nSpawning connections:\n");
+       for (sp = Spawn; sp; sp = sp->next) {
+               sa = (struct sockaddr_in *)&sp->source;
+               bf = inet_ntop(AF_INET, &sa->sin_addr, buf, sizeof buf);
+               if (!bf)  {
+                       logit(LOG_WARNING, "inet_ntop");
+                       bf = "?";
+               }
+               fprintf(fp, "fd %d: state %d, from %s:%d\n",
+                       sp->fd, sp->inlen + (sp->reading_msg ? sp->msglen : 0),
+                       bf, sa->sin_port);
+       }
+}
diff --git a/games/hunt/huntd/conf.c b/games/hunt/huntd/conf.c
new file mode 100644 (file)
index 0000000..f309f80
--- /dev/null
@@ -0,0 +1,318 @@
+/* 
+ * David Leonard <d@openbsd.org>, 1999. Public domain.
+ *
+ * $OpenBSD: conf.c,v 1.7 2007/03/20 03:43:50 tedu Exp $
+ * $DragonFly: src/games/hunt/huntd/conf.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+#include <sys/param.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include "hunt.h"
+#include "server.h"
+#include "conf.h"
+
+/* Configuration option variables for the server: */
+
+int conf_random =      1;
+int conf_reflect =     1;
+int conf_monitor =     1;
+int conf_ooze =                1;
+int conf_fly =         1;
+int conf_volcano =     1;
+int conf_drone =       1;
+int conf_boots =       1;
+int conf_scan =                1;
+int conf_cloak =       1;
+int conf_logerr =      1;
+int conf_syslog =      0;
+
+int conf_scoredecay =  15;
+int conf_maxremove =   40;
+int conf_linger =      90;
+
+int conf_flytime =     20;
+int conf_flystep =     5;
+int conf_volcano_max = 50;
+int conf_ptrip_face =  2;
+int conf_ptrip_back =  95;
+int conf_ptrip_side =  50;
+int conf_prandom =     1;
+int conf_preflect =    1;
+int conf_pshot_coll =  5;
+int conf_pgren_coll =  10;
+int conf_pgren_catch = 10;
+int conf_pmiss =       5;
+int conf_pdroneabsorb =        1;
+int conf_fall_frac =   5;
+
+int conf_bulspd =      5;
+int conf_ishots =      15;
+int conf_nshots =      5;
+int conf_maxncshot =   2;
+int conf_maxdam =      10;
+int conf_mindam =      5;
+int conf_stabdam =     2;
+int conf_killgain =    2;
+int conf_slimefactor = 3;
+int conf_slimespeed =  5;
+int conf_lavaspeed =   1;
+int conf_cloaklen =    20;
+int conf_scanlen =     20;
+int conf_mindshot =    2;
+int conf_simstep =     0;
+
+
+struct kwvar {
+       const char *    kw;
+       void *  var;
+       enum vartype { Vint, Vchar, Vstring, Vdouble } type;
+};
+
+static struct kwvar keywords[] = {
+       { "random",             &conf_random,           Vint },
+       { "reflect",            &conf_reflect,          Vint },
+       { "monitor",            &conf_monitor,          Vint },
+       { "ooze",               &conf_ooze,             Vint },
+       { "fly",                &conf_fly,              Vint },
+       { "volcano",            &conf_volcano,          Vint },
+       { "drone",              &conf_drone,            Vint },
+       { "boots",              &conf_boots,            Vint },
+       { "scan",               &conf_scan,             Vint },
+       { "cloak",              &conf_cloak,            Vint },
+       { "logerr",             &conf_logerr,           Vint },
+       { "syslog",             &conf_syslog,           Vint },
+       { "scoredecay",         &conf_scoredecay,       Vint },
+       { "maxremove",          &conf_maxremove,        Vint },
+       { "linger",             &conf_linger,           Vint },
+
+       { "flytime",            &conf_flytime,          Vint },
+       { "flystep",            &conf_flystep,          Vint },
+       { "volcano_max",        &conf_volcano_max,      Vint },
+       { "ptrip_face",         &conf_ptrip_face,       Vint },
+       { "ptrip_back",         &conf_ptrip_back,       Vint },
+       { "ptrip_side",         &conf_ptrip_side,       Vint },
+       { "prandom",            &conf_prandom,          Vint },
+       { "preflect",           &conf_preflect,         Vint },
+       { "pshot_coll",         &conf_pshot_coll,       Vint },
+       { "pgren_coll",         &conf_pgren_coll,       Vint },
+       { "pgren_catch",        &conf_pgren_catch,      Vint },
+       { "pmiss",              &conf_pmiss,            Vint },
+       { "pdroneabsorb",       &conf_pdroneabsorb,     Vint },
+       { "fall_frac",          &conf_fall_frac,        Vint },
+
+       { "bulspd",             &conf_bulspd,           Vint },
+       { "ishots",             &conf_ishots,           Vint },
+       { "nshots",             &conf_nshots,           Vint },
+       { "maxncshot",          &conf_maxncshot,        Vint },
+       { "maxdam",             &conf_maxdam,           Vint },
+       { "mindam",             &conf_mindam,           Vint },
+       { "stabdam",            &conf_stabdam,          Vint },
+       { "killgain",           &conf_killgain,         Vint },
+       { "slimefactor",        &conf_slimefactor,      Vint },
+       { "slimespeed",         &conf_slimespeed,       Vint },
+       { "lavaspeed",          &conf_lavaspeed,        Vint },
+       { "cloaklen",           &conf_cloaklen,         Vint },
+       { "scanlen",            &conf_scanlen,          Vint },
+       { "mindshot",           &conf_mindshot,         Vint },
+       { "simstep",            &conf_simstep,          Vint },
+
+       { NULL, NULL, Vint }
+};
+
+static char *
+parse_int(char *p, struct kwvar *kvp, const char *fnm, int *linep)
+{
+       char *valuestart, *digitstart;
+       char savec;
+       int newval;
+
+       /* expect a number */
+       valuestart = p;
+       if (*p == '-') 
+               p++;
+       digitstart = p;
+       while (isdigit(*p))
+               p++;
+       if ((*p == '\0' || isspace(*p) || *p == '#') && digitstart != p) {
+               savec = *p;
+               *p = '\0';
+               newval = atoi(valuestart);
+               *p = savec;
+               logx(LOG_INFO, "%s:%d: %s: %d -> %d", 
+                       fnm, *linep, kvp->kw, *(int *)kvp->var, newval);
+               *(int *)kvp->var = newval;
+               return p;
+       } else {
+               logx(LOG_ERR, "%s:%d: invalid integer value \"%s\"", 
+                   fnm, *linep, valuestart);
+               return NULL;
+       }
+}
+
+static char *
+parse_value(char *p, struct kwvar *kvp, const char *fnm, int *linep)
+{
+
+       switch (kvp->type) {
+       case Vint:
+               return parse_int(p, kvp, fnm, linep);
+       case Vchar:
+       case Vstring:
+       case Vdouble:
+               /* tbd */
+       default:
+               abort();
+       }
+}
+
+static void
+parse_line(char *buf, const char *fnm, int *line)
+{
+       char *p;
+       char *word;
+       char *endword;
+       struct kwvar *kvp;
+       char savec;
+
+       p = buf;
+
+       /* skip leading white */
+       while (isspace(*p))
+               p++;
+       /* allow blank lines and comment lines */
+       if (*p == '\0' || *p == '#')
+               return;
+
+       /* walk to the end of the word: */
+       word = p;
+       if (isalpha(*p) || *p == '_') {
+               p++;
+               while (isalpha(*p) || isdigit(*p) || *p == '_')
+                       p++;
+       }
+       endword = p;
+
+       if (endword == word) {
+               logx(LOG_ERR, "%s:%d: expected variable name",
+                       fnm, *line);
+               return;
+       }
+
+       /* match the configuration variable name */
+       savec = *endword;
+       *endword = '\0';
+       for (kvp = keywords; kvp->kw; kvp++) 
+               if (strcmp(kvp->kw, word) == 0)
+                       break;
+       *endword = savec;
+
+       if (kvp->kw == NULL) {
+               logx(LOG_ERR, 
+                   "%s:%d: unrecognised variable \"%.*s\"", 
+                   fnm, *line, endword - word, word);
+               return;
+       }
+
+       /* skip whitespace */
+       while (isspace(*p))
+               p++;
+
+       if (*p++ != '=') {
+               logx(LOG_ERR, "%s:%d: expected `=' after %s", fnm, *line, word);
+               return;
+       }
+
+       /* skip whitespace */
+       while (isspace(*p))
+               p++;
+
+       /* parse the value */
+       p = parse_value(p, kvp, fnm, line);
+       if (!p) 
+               return;
+
+       /* skip trailing whitespace */
+       while (isspace(*p))
+               p++;
+
+       if (*p && *p != '#') {
+               logx(LOG_WARNING, "%s:%d: trailing garbage ignored",
+                       fnm, *line);
+       }
+}
+
+
+static void
+load_config(FILE *f, char *fnm)
+{
+       char buf[BUFSIZ];
+       size_t len;
+       int line;
+       char *p;
+
+       line = 0;
+       while ((p = fgetln(f, &len)) != NULL) {
+               line++;
+               if (p[len-1] == '\n')
+                       len--;
+               if (len >= sizeof(buf)) {
+                       logx(LOG_ERR, "%s:%d: line too long", fnm, line);
+                       continue;
+               }
+               (void)memcpy(buf, p, len);
+               buf[len] = '\0';
+               parse_line(buf, fnm, &line);
+       }
+}
+
+/*
+ * load various config file, allowing later ones to 
+ * overwrite earlier values
+ */
+void
+config()
+{
+       const char *home;
+       char nm[MAXPATHLEN + 1];
+       static const char *fnms[] = { 
+               "/etc/hunt.conf",
+               "%s/.hunt.conf", 
+               ".hunt.conf", 
+               NULL
+       };
+       int fn;
+       FILE *f;
+
+       /* All the %s's get converted to $HOME */
+       if ((home = getenv("HOME")) == NULL)
+               home = "";
+
+       for (fn = 0; fnms[fn]; fn++) {
+               snprintf(nm, sizeof nm, fnms[fn], home);
+               if ((f = fopen(nm, "r")) != NULL) {
+                       load_config(f, nm);
+                       fclose(f);
+               } 
+               else if (errno != ENOENT)
+                       logit(LOG_WARNING, "%s", nm);
+       }
+}
+
+/*
+ * Parse a single configuration argument given on the command line
+ */
+void
+config_arg(arg)
+       char *arg;
+{
+       int line = 0;
+
+       parse_line(arg, "*Initialisation*", &line);
+}
diff --git a/games/hunt/huntd/conf.h b/games/hunt/huntd/conf.h
new file mode 100644 (file)
index 0000000..9f3da24
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * David Leonard <d@openbsd.org>, 1999.  Public domain.
+ *
+ * $OpenBSD: conf.h,v 1.5 2003/06/17 00:36:40 pjanzen Exp $
+ * $DragonFly: src/games/hunt/huntd/conf.h,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+/* Configuration option variables for the server: */
+
+extern int conf_random;
+extern int conf_reflect;
+extern int conf_monitor;
+extern int conf_ooze;
+extern int conf_fly;
+extern int conf_volcano;
+extern int conf_drone;
+extern int conf_boots;
+extern int conf_scan;
+extern int conf_cloak;
+extern int conf_logerr;
+extern int conf_syslog;
+
+extern int conf_scoredecay;
+extern int conf_maxremove;
+extern int conf_linger;
+
+extern int conf_flytime;
+extern int conf_flystep;
+extern int conf_volcano_max;
+extern int conf_ptrip_face;
+extern int conf_ptrip_back;
+extern int conf_ptrip_side;
+extern int conf_prandom;
+extern int conf_preflect;
+extern int conf_pshot_coll;
+extern int conf_pgren_coll;
+extern int conf_pgren_catch;
+extern int conf_pmiss;
+extern int conf_pdroneabsorb;
+extern int conf_fall_frac;
+
+extern int conf_bulspd;
+extern int conf_ishots;
+extern int conf_nshots;
+extern int conf_maxncshot;
+extern int conf_maxdam;
+extern int conf_mindam;
+extern int conf_stabdam;
+extern int conf_killgain;
+extern int conf_slimefactor;
+extern int conf_slimespeed;
+extern int conf_lavaspeed;
+extern int conf_cloaklen;
+extern int conf_scanlen;
+extern int conf_mindshot;
+extern int conf_simstep;
+
+void config(void);
+void config_arg(char *);
diff --git a/games/hunt/huntd/draw.c b/games/hunt/huntd/draw.c
new file mode 100644 (file)
index 0000000..ce4cdf8
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: draw.c,v 1.7 2003/06/11 08:45:33 pjanzen Exp $
+ * $NetBSD: draw.c,v 1.2 1997/10/10 16:33:04 lukem Exp $
+ * $DragonFly: src/games/hunt/huntd/draw.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+#include <string.h>
+
+#include "hunt.h"
+#include "server.h"
+#include "conf.h"
+
+static char    translate(char);
+static int     player_sym(PLAYER *, int, int);
+static void    drawstatus(PLAYER *);
+static void    see(PLAYER *, int);
+
+/*
+ * drawmaze:
+ *     Draw the entire maze on a player's screen.
+ */
+void
+drawmaze(pp)
+       PLAYER  *pp;
+{
+       int     x;
+       char    *sp;
+       int     y;
+       char    *endp;
+
+       /* Clear the client's screen: */
+       clrscr(pp);
+       /* Draw the top row of the maze: */
+       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);
+                               /* Draw the player as themselves */
+                               if (pp->p_x == x && pp->p_y == y)
+                                       outch(pp, translate(*sp));
+                               /* Possibly draw other players as team nrs */
+                               else if (is_player(*sp))
+                                       outch(pp, player_sym(pp, y, x));
+                               else
+                                       outch(pp, *sp);
+                       }
+       }
+       /* Draw the last row of the maze: */
+       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)
+ */
+static void
+drawstatus(pp)
+       PLAYER  *pp;
+{
+       int     i;
+       PLAYER  *np;
+
+       outyx(pp, STAT_AMMO_ROW, STAT_LABEL_COL, "Ammo:");
+       ammo_update(pp);
+
+       outyx(pp, STAT_GUN_ROW,  STAT_LABEL_COL, "Gun:");
+       outyx(pp, STAT_GUN_ROW,  STAT_VALUE_COL, "%3s",
+               (pp->p_ncshot < conf_maxncshot) ? "ok" : "");
+
+       outyx(pp, STAT_DAM_ROW,  STAT_LABEL_COL, "Damage:");
+       outyx(pp, STAT_DAM_ROW,  STAT_VALUE_COL, "%2d/%2d",
+               pp->p_damage, pp->p_damcap);
+
+       outyx(pp, STAT_KILL_ROW, STAT_LABEL_COL, "Kills:");
+       outyx(pp, STAT_KILL_ROW, STAT_VALUE_COL, "%3d",
+               (pp->p_damcap - conf_maxdam) / 2);
+
+       outyx(pp, STAT_PLAY_ROW, STAT_LABEL_COL, "Player:");
+       for (i = STAT_PLAY_ROW + 1, np = Player; np < End_player; np++, i++) {
+               outyx(pp, i, STAT_NAME_COL, "%5.2f%c%-10.10s %c", 
+                       np->p_ident->i_score, stat_char(np), 
+                       np->p_ident->i_name, np->p_ident->i_team);
+       }
+
+       outyx(pp, STAT_MON_ROW, STAT_LABEL_COL, "Monitor:");
+       for (i = STAT_MON_ROW + 1, np = Monitor; np < End_monitor; np++, i++) {
+               outyx(pp, i++, STAT_NAME_COL, "%5.5s %-10.10s %c",
+                       " ", np->p_ident->i_name, np->p_ident->i_team);
+       }
+}
+
+/*
+ * look
+ *     check and update the visible area around the player
+ */
+void
+look(pp)
+       PLAYER  *pp;
+{
+       int     x, y;
+
+       x = pp->p_x;
+       y = pp->p_y;
+
+       /*
+        * The player is aware of all objects immediately adjacent to
+        * their position:
+        */
+       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) {
+         /* The player can see down corridors in directions except behind: */
+         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;
+         /* But they don't see too far when they are flying about: */
+         case FLYER:
+               break;
+       }
+
+       /* Move the cursor back over the player: */
+       cgoto(pp, y, x);
+}
+
+/*
+ * see
+ *     Look down a corridor, or towards an open space. This
+ *     is a simulation of visibility from the player's perspective.
+ */
+static void
+see(pp, face)
+       PLAYER  *pp;
+       int     face;
+{
+       char    *sp;
+       int     y, x;
+
+       /* Start from the player's position: */
+       x = pp->p_x;
+       y = pp->p_y;
+
+       #define seewalk(dx, dy)                                         \
+               x += (dx);                                              \
+               y += (dy);                                              \
+               sp = &Maze[y][x];                                       \
+               while (See_over[(int)*sp]) {                            \
+                       x += (dx);                                      \
+                       y += (dy);                                      \
+                       sp += ((dx) + (dy) * sizeof Maze[0]);           \
+                       check(pp, y + dx, x + dy);                      \
+                       check(pp, y, x);                                \
+                       check(pp, y - dx, x - dy);                      \
+               }
+
+       switch (face) {
+         case LEFTS:
+               seewalk(-1, 0); break;
+         case RIGHT:
+               seewalk(1, 0); break;
+         case ABOVE:
+               seewalk(0, -1); break;
+         case BELOW:
+               seewalk(0, 1); break;
+       }
+}
+
+/*
+ * check
+ *     The player is aware of a cell in the maze.
+ *     Ensure it is shown properly on their screen.
+ */
+void
+check(pp, y, x)
+       PLAYER  *pp;
+       int     y, x;
+{
+       int     i;
+       int     ch;
+       PLAYER  *rpp;
+
+       if (pp == ALL_PLAYERS) {
+               for (pp = Player; pp < End_player; pp++)
+                       check(pp, y, x);
+               for (pp = Monitor; pp < End_monitor; pp++)
+                       check(pp, y, x);
+               return;
+       }
+
+       i = y * sizeof Maze[0] + x;
+       ch = ((char *) Maze)[i];
+       if (ch != ((char *) pp->p_maze)[i]) {
+               rpp = pp;
+               cgoto(rpp, y, x);
+               if (x == rpp->p_x && y == rpp->p_y)
+                       outch(rpp, translate(ch));
+               else if (is_player(ch))
+                       outch(rpp, player_sym(rpp, y, x));
+               else
+                       outch(rpp, ch);
+               ((char *) rpp->p_maze)[i] = ch;
+       }
+}
+
+/*
+ * showstat
+ *     Update the status of a player on everyone's screen
+ */
+void
+showstat(pp)
+       PLAYER  *pp;
+{
+
+       outyx(ALL_PLAYERS, 
+               STAT_PLAY_ROW + 1 + (pp - Player), STAT_SCAN_COL,
+               "%c", stat_char(pp));
+}
+
+/*
+ * drawplayer:
+ *     Draw the player on the screen and show him to everyone who's scanning
+ *     unless he is cloaked.
+ *     The 'draw' flag when false, causes the 'saved under' character to
+ *     be drawn instead of the player; effectively un-drawing the player.
+ */
+void
+drawplayer(pp, draw)
+       PLAYER  *pp;
+       FLAG    draw;
+{
+       PLAYER  *newp;
+       int     x, y;
+
+       x = pp->p_x;
+       y = pp->p_y;
+
+       /* Draw or un-draw the player into the master map: */
+       Maze[y][x] = draw ? pp->p_face : pp->p_over;
+
+       /* The monitors can always see this player: */
+       for (newp = Monitor; newp < End_monitor; newp++)
+               check(newp, y, x);
+
+       /* Check if other players can see this player: */
+       for (newp = Player; newp < End_player; newp++) {
+               if (!draw) {
+                       /* When un-drawing, show everyone what was under */
+                       check(newp, y, x);
+                       continue;
+               }
+               if (newp == pp) {
+                       /* The player can always see themselves: */
+                       check(newp, y, x);
+                       continue;
+               }
+               /* Check if the other player just run out of scans */
+               if (newp->p_scan == 0) {
+                       /* The other player is no longer scanning: */
+                       newp->p_scan--;
+                       showstat(newp);
+               /* Check if the other play is scannning */
+               } else if (newp->p_scan > 0) {
+                       /* If this player's not cloacked, draw him: */
+                       if (pp->p_cloak < 0)
+                               check(newp, y, x);
+                       /* And this uses up a scan. */
+                       newp->p_scan--;
+               }
+       }
+
+       /* Use up one point of cloak time when drawing: */
+       if (draw && pp->p_cloak >= 0) {
+               pp->p_cloak--;
+               /* Check if we ran out of cloak: */
+               if (pp->p_cloak < 0)
+                       showstat(pp);
+       }
+}
+
+/*
+ * message:
+ *     Write a message at the bottom of the screen.
+ */
+void
+message(PLAYER *pp, const char *s)
+{
+       cgoto(pp, HEIGHT, 0);
+       outstr(pp, s, strlen(s));
+       ce(pp);
+}
+
+/*
+ * translate:
+ *     Turn a player character into a more personal player character.
+ *     ie: {,},!,i becomes <,>,v,^
+ */
+static char
+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 symbol for the player at (y,x) when viewed by player 'pp'.
+ *     ie: - unteamed players appear as {,},!,i
+ *         - unteamed monitors see all players as team digits
+ *         - teamed players see other players on their team, as a digit
+ */
+static int
+player_sym(pp, y, x)
+       PLAYER  *pp;
+       int     y, x;
+{
+       PLAYER  *npp;
+
+       npp = play_at(y, x);
+       if (npp->p_ident->i_team == ' ')
+               return Maze[y][x];
+       if (pp->p_ident->i_team == '*')
+               return npp->p_ident->i_team;
+       if (pp->p_ident->i_team != npp->p_ident->i_team)
+               return Maze[y][x];
+       return pp->p_ident->i_team;
+}
diff --git a/games/hunt/huntd/driver.c b/games/hunt/huntd/driver.c
new file mode 100644 (file)
index 0000000..8043fbb
--- /dev/null
@@ -0,0 +1,1214 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: driver.c,v 1.17 2007/04/02 14:55:16 jmc Exp $
+ * $NetBSD: driver.c,v 1.5 1997/10/20 00:37:16 lukem Exp $
+ * $DragonFly: src/games/hunt/huntd/driver.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <tcpd.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <paths.h>
+#include <fcntl.h>
+#include "hunt.h"
+#include "conf.h"
+#include "server.h"
+
+char   *First_arg;             /* pointer to argv[0] */
+u_int16_t Server_port;
+int    Server_socket;          /* test socket to answer datagrams */
+FLAG   should_announce = 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 */
+in_addr_t Server_addr = INADDR_ANY;    /* address to bind to */
+
+static void    clear_scores(void);
+static int     havechar(PLAYER *);
+static void    init(void);
+       int     main(int, char *[]);
+static void    makeboots(void);
+static void    send_stats(void);
+static void    zap(PLAYER *, FLAG);
+static  void   announce_game(void);
+static void    siginfo(int);
+static void    print_stats(FILE *);
+static void    handle_wkport(int);
+
+/*
+ * main:
+ *     The main program.
+ */
+int
+main(ac, av)
+       int     ac;
+       char    **av;
+{
+       PLAYER          *pp;
+       int             had_char;
+       static fd_set   read_fds;
+       static FLAG     first = TRUE;
+       static FLAG     server = FALSE;
+       int             c;
+       static struct timeval   linger = { 0, 0 };
+       static struct timeval   timeout = { 0, 0 }, *to;
+       struct spawn    *sp, *spnext;
+       int             ret;
+       int             nready;
+       int             fd;
+
+       First_arg = av[0];
+
+       config();
+
+       while ((c = getopt(ac, av, "sp:a:D:")) != -1) {
+               switch (c) {
+                 case 's':
+                       server = TRUE;
+                       break;
+                 case 'p':
+                       should_announce = FALSE;
+                       Server_port = atoi(optarg);
+                       break;
+                 case 'a':
+                       if (!inet_aton(optarg, (struct in_addr *)&Server_addr))
+                               err(1, "bad interface address: %s", optarg);
+                       break;
+                 case 'D':
+                       config_arg(optarg);
+                       break;
+                 default:
+erred:
+                       fprintf(stderr,
+                           "usage: %s [-s] [-a addr] [-Dvar=value ...] "
+                           "[-p port]\n",
+                           av[0]);
+                       exit(2);
+               }
+       }
+       if (optind < ac)
+               goto erred;
+
+       /* Open syslog: */
+       openlog("huntd", LOG_PID | (conf_logerr && !server? LOG_PERROR : 0),
+               LOG_DAEMON);
+
+       /* Initialise game parameters: */
+       init();
+
+again:
+       do {
+               /* First, poll to see if we can get input */
+               do {
+                       read_fds = Fds_mask;
+                       errno = 0;
+                       timerclear(&timeout);
+                       nready = select(Num_fds, &read_fds, NULL, NULL, 
+                           &timeout);
+                       if (nready < 0 && errno != EINTR) {
+                               logit(LOG_ERR, "select");
+                               cleanup(1);
+                       }
+               } while (nready < 0);
+
+               if (nready == 0) {
+                       /*
+                        * Nothing was ready. We do some work now
+                        * to see if the simulation has any pending work
+                        * to do, and decide if we need to block 
+                        * indefinitely or just timeout.
+                        */
+                       do {
+                               if (conf_simstep && can_moveshots()) {
+                               /*
+                                * block for a short time before continuing
+                                * with explosions, bullets and whatnot
+                                */
+                                       to = &timeout;
+                                       to->tv_sec =  conf_simstep / 1000000;
+                                       to->tv_usec = conf_simstep % 1000000;
+                               } else
+                               /*
+                                * since there's nothing going on,
+                                * just block waiting for external activity
+                                */
+                                       to = NULL;
+                               
+                               read_fds = Fds_mask;
+                               errno = 0;
+                               nready = select(Num_fds, &read_fds, NULL, NULL, 
+                                   to);
+                               if (nready < 0 && errno != EINTR) {
+                                       logit(LOG_ERR, "select");
+                                       cleanup(1);
+                               }
+                       } while (nready < 0);
+               }
+
+               /* Remember which descriptors are active: */
+               Have_inp = read_fds;
+
+               /* Answer new player connections: */
+               if (FD_ISSET(Socket, &Have_inp))
+                       answer_first();
+
+               /* Continue answering new player connections: */
+               for (sp = Spawn; sp; ) {
+                       spnext = sp->next;
+                       fd = sp->fd;
+                       if (FD_ISSET(fd, &Have_inp) && answer_next(sp)) {
+                               /*
+                                * Remove from the spawn list. (fd remains in 
+                                * read set).
+                                */
+                               *sp->prevnext = sp->next;
+                               if (sp->next)
+                                       sp->next->prevnext = sp->prevnext;
+                               free(sp);
+
+                               /* We probably consumed all data. */
+                               FD_CLR(fd, &Have_inp);
+
+                               /* Announce game if this is the first spawn. */
+                               if (first && should_announce)
+                                       announce_game();
+                               first = FALSE;
+                       } 
+                       sp = spnext;
+               }
+
+               /* Process input and move bullets until we've exhausted input */
+               had_char = TRUE;
+               while (had_char) {
+
+                       moveshots();
+                       for (pp = Player; pp < End_player; )
+                               if (pp->p_death[0] != '\0')
+                                       zap(pp, TRUE);
+                               else
+                                       pp++;
+                       for (pp = Monitor; pp < End_monitor; )
+                               if (pp->p_death[0] != '\0')
+                                       zap(pp, FALSE);
+                               else
+                                       pp++;
+
+                       had_char = FALSE;
+                       for (pp = Player; pp < End_player; pp++)
+                               if (havechar(pp)) {
+                                       execute(pp);
+                                       pp->p_nexec++;
+                                       had_char = TRUE;
+                               }
+                       for (pp = Monitor; pp < End_monitor; pp++)
+                               if (havechar(pp)) {
+                                       mon_execute(pp);
+                                       pp->p_nexec++;
+                                       had_char = TRUE;
+                               }
+               }
+
+               /* Handle a datagram sent to the server socket: */
+               if (FD_ISSET(Server_socket, &Have_inp))
+                       handle_wkport(Server_socket);
+
+               /* Answer statistics connections: */
+               if (FD_ISSET(Status, &Have_inp))
+                       send_stats();
+
+               /* Flush/synchronize all the displays: */
+               for (pp = Player; pp < End_player; pp++) {
+                       if (FD_ISSET(pp->p_fd, &read_fds)) {
+                               sendcom(pp, READY, pp->p_nexec);
+                               pp->p_nexec = 0;
+                       }
+                       flush(pp);
+               }
+               for (pp = Monitor; pp < End_monitor; pp++) {
+                       if (FD_ISSET(pp->p_fd, &read_fds)) {
+                               sendcom(pp, READY, pp->p_nexec);
+                               pp->p_nexec = 0;
+                       }
+                       flush(pp);
+               }
+       } while (Nplayer > 0);
+
+       /* No more players! */
+
+       /* No players yet or a continuous game? */
+       if (first || conf_linger < 0)
+               goto again;
+
+       /* Wait a short while for one to come back: */
+       read_fds = Fds_mask;
+       linger.tv_sec = conf_linger;
+       while ((ret = select(Num_fds, &read_fds, NULL, NULL, &linger)) < 0) {
+               if (errno != EINTR) {
+                       logit(LOG_WARNING, "select");
+                       break;
+               }
+               read_fds = Fds_mask;
+               linger.tv_sec = conf_linger;
+               linger.tv_usec = 0;
+       }
+       if (ret > 0)
+               /* Someone returned! Resume the game: */
+               goto again;
+       /* else, it timed out, and the game is really over. */
+
+       /* If we are an inetd server, we should re-init the map and restart: */
+       if (server) {
+               clear_scores();
+               makemaze();
+               clearwalls();
+               makeboots();
+               first = TRUE;
+               goto again;
+       }
+
+       /* Get rid of any attached monitors: */
+       for (pp = Monitor; pp < End_monitor; )
+               zap(pp, FALSE);
+
+       /* Fin: */
+       cleanup(0);
+       exit(0);
+}
+
+/*
+ * init:
+ *     Initialize the global parameters.
+ */
+static void
+init()
+{
+       int     i;
+       struct sockaddr_in      test_port;
+       int     true = 1;
+       socklen_t       len;
+       struct sockaddr_in      addr;
+       struct sigaction        sact;
+       struct servent *se;
+
+       (void) setsid();
+       if (setpgid(getpid(), getpid()) == -1)
+               err(1, "setpgid");
+
+       sact.sa_flags = SA_RESTART;
+       sigemptyset(&sact.sa_mask);
+
+       /* Ignore HUP, QUIT and PIPE: */
+       sact.sa_handler = SIG_IGN;
+       if (sigaction(SIGHUP, &sact, NULL) == -1)
+               err(1, "sigaction SIGHUP");
+       if (sigaction(SIGQUIT, &sact, NULL) == -1)
+               err(1, "sigaction SIGQUIT");
+       if (sigaction(SIGPIPE, &sact, NULL) == -1)
+               err(1, "sigaction SIGPIPE");
+
+       /* Clean up gracefully on INT and TERM: */
+       sact.sa_handler = cleanup;
+       if (sigaction(SIGINT, &sact, NULL) == -1)
+               err(1, "sigaction SIGINT");
+       if (sigaction(SIGTERM, &sact, NULL) == -1)
+               err(1, "sigaction SIGTERM");
+
+       /* Handle INFO: */
+       sact.sa_handler = siginfo;
+       if (sigaction(SIGINFO, &sact, NULL) == -1)
+               err(1, "sigaction SIGINFO");
+
+       if (chdir("/") == -1)
+               warn("chdir");
+       (void) umask(0777);
+
+       /* Initialize statistics socket: */
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = Server_addr;
+       addr.sin_port = 0;
+
+       Status = socket(AF_INET, SOCK_STREAM, 0);
+       if (bind(Status, (struct sockaddr *) &addr, sizeof addr) < 0) {
+               logit(LOG_ERR, "bind");
+               cleanup(1);
+       }
+       if (listen(Status, 5) == -1) {
+               logit(LOG_ERR, "listen");
+               cleanup(1);
+       }
+
+       len = sizeof (struct sockaddr_in);
+       if (getsockname(Status, (struct sockaddr *) &addr, &len) < 0)  {
+               logit(LOG_ERR, "getsockname");
+               cleanup(1);
+       }
+       stat_port = ntohs(addr.sin_port);
+
+       /* Initialize main socket: */
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = Server_addr;
+       addr.sin_port = 0;
+
+       Socket = socket(AF_INET, SOCK_STREAM, 0);
+
+       if (bind(Socket, (struct sockaddr *) &addr, sizeof addr) < 0) {
+               logit(LOG_ERR, "bind");
+               cleanup(1);
+       }
+       if (listen(Socket, 5) == -1) {
+               logit(LOG_ERR, "listen");
+               cleanup(1);
+       }
+
+       len = sizeof (struct sockaddr_in);
+       if (getsockname(Socket, (struct sockaddr *) &addr, &len) < 0)  {
+               logit(LOG_ERR, "getsockname");
+               cleanup(1);
+       }
+       sock_port = ntohs(addr.sin_port);
+
+       /* Initialize minimal select mask */
+       FD_ZERO(&Fds_mask);
+       FD_SET(Socket, &Fds_mask);
+       FD_SET(Status, &Fds_mask);
+       Num_fds = ((Socket > Status) ? Socket : Status) + 1;
+
+       /* Find the port that huntd should run on */
+       if (Server_port == 0) {
+               se = getservbyname("hunt", "udp");
+               if (se != NULL)
+                       Server_port = ntohs(se->s_port);
+               else
+                       Server_port = HUNT_PORT;
+       }
+
+       /* Check if stdin is a socket: */
+       len = sizeof (struct sockaddr_in);
+       if (getsockname(STDIN_FILENO, (struct sockaddr *) &test_port, &len) >= 0
+           && test_port.sin_family == AF_INET) {
+               /* We are probably running from inetd:  don't log to stderr */
+               Server_socket = STDIN_FILENO;
+               conf_logerr = 0;
+               if (test_port.sin_port != htons((u_short) Server_port)) {
+                       /* Private game */
+                       should_announce = FALSE;
+                       Server_port = ntohs(test_port.sin_port);
+               }
+       } else {
+               /* We need to listen on a socket: */
+               test_port = addr;
+               test_port.sin_port = htons((u_short) Server_port);
+
+               Server_socket = socket(AF_INET, SOCK_DGRAM, 0);
+
+               /* Permit multiple huntd's on the same port. */
+               if (setsockopt(Server_socket, SOL_SOCKET, SO_REUSEPORT, &true, 
+                   sizeof true) < 0)
+                       logit(LOG_ERR, "setsockopt SO_REUSEADDR");
+
+               if (bind(Server_socket, (struct sockaddr *) &test_port,
+                   sizeof test_port) < 0) {
+                       logit(LOG_ERR, "bind port %d", Server_port);
+                       cleanup(1);
+               }
+
+               /* Datagram sockets do not need a listen() call. */
+       }
+
+       /* We'll handle the broadcast listener in the main loop: */
+       FD_SET(Server_socket, &Fds_mask);
+       if (Server_socket + 1 > Num_fds)
+               Num_fds = Server_socket + 1;
+
+       /* Initialise the random seed: */
+       srandomdev();
+
+       /* Dig the maze: */
+       makemaze();
+
+       /* Create some boots, if needed: */
+       makeboots();
+
+       /* Construct a table of what objects a player can see over: */
+       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;
+       See_over[WALL4] = FALSE;
+       See_over[WALL5] = FALSE;
+
+       logx(LOG_INFO, "game started");
+}
+
+/*
+ * makeboots:
+ *     Put the boots in the maze
+ */
+static void
+makeboots()
+{
+       int     x, y;
+       PLAYER  *pp;
+
+       if (conf_boots) {
+               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;
+}
+
+
+/*
+ * checkdam:
+ *     Apply damage to the victim from an attacker.
+ *     If the victim dies as a result, give points to 'credit',
+ */
+void
+checkdam(victim, attacker, credit, damage, stype)
+       PLAYER  *victim, *attacker;
+       IDENT   *credit;
+       int     damage;
+       char    stype;
+{
+       const char      *cp;
+       int     y;
+
+       /* Don't do anything if the victim is already in the throes of death */
+       if (victim->p_death[0] != '\0')
+               return;
+
+       /* Weaken slime attacks by 0.5 * number of boots the victim has on: */
+       if (stype == SLIME)
+               switch (victim->p_nboots) {
+                 default:
+                       break;
+                 case 1:
+                       damage = (damage + 1) / 2;
+                       break;
+                 case 2:
+                       if (attacker != NULL)
+                               message(attacker, "He has boots on!");
+                       return;
+               }
+
+       /* The victim sustains some damage: */
+       victim->p_damage += damage;
+
+       /* Check if the victim survives the hit: */
+       if (victim->p_damage <= victim->p_damcap) {
+               /* They survive. */
+               outyx(victim, STAT_DAM_ROW, STAT_VALUE_COL, "%2d",
+                       victim->p_damage);
+               return;
+       }
+
+       /* Describe how the victim died: */
+       switch (stype) {
+         default:
+               cp = "Killed";
+               break;
+         case FALL:
+               cp = "Killed on impact";
+               break;
+         case KNIFE:
+               cp = "Stabbed to death";
+               victim->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;
+         case SLIME:
+               cp = "Slimed";
+               if (credit != NULL)
+                       credit->i_slime++;
+               break;
+         case LAVA:
+               cp = "Baked";
+               break;
+         case DSHOT:
+               cp = "Eliminated";
+               break;
+       }
+
+       if (credit == NULL) {
+               const char *blame;
+
+               /*
+                * Nobody is taking the credit for the kill.
+                * Attribute it to either a mine or 'act of God'.
+                */
+               switch (stype) {
+               case MINE:
+               case GMINE:
+                       blame = "a mine";
+                       break;
+               default:
+                       blame = "act of God";
+                       break;
+               }
+
+               /* Set the death message: */
+               (void) snprintf(victim->p_death, sizeof victim->p_death, 
+                       "| %s by %s |", cp, blame);
+
+               /* No further score crediting needed. */
+               return;
+       }
+
+       /* Set the death message: */
+       (void) snprintf(victim->p_death, sizeof victim->p_death, 
+               "| %s by %s |", cp, credit->i_name);
+
+       if (victim == attacker) {
+               /* No use killing yourself. */
+               credit->i_kills--;
+               credit->i_bkills++;
+       } 
+       else if (victim->p_ident->i_team == ' '
+           || victim->p_ident->i_team != credit->i_team) {
+               /* A cross-team kill: */
+               credit->i_kills++;
+               credit->i_gkills++;
+       }
+       else {
+               /* They killed someone on the same team: */
+               credit->i_kills--;
+               credit->i_bkills++;
+       }
+
+       /* Compute the new credited score: */
+       credit->i_score = credit->i_kills / (double) credit->i_entries;
+
+       /* The victim accrues one death: */
+       victim->p_ident->i_deaths++;
+
+       /* Account for 'Stillborn' deaths */
+       if (victim->p_nchar == 0)
+               victim->p_ident->i_stillb++;
+
+       if (attacker) {
+               /* Give the attacker player a bit more strength */
+               attacker->p_damcap += conf_killgain;
+               attacker->p_damage -= conf_killgain;
+               if (attacker->p_damage < 0)
+                       attacker->p_damage = 0;
+
+               /* Tell the attacker his new strength: */
+               outyx(attacker, STAT_DAM_ROW, STAT_VALUE_COL, "%2d/%2d", 
+                       attacker->p_damage, attacker->p_damcap);
+
+               /* Tell the attacker his new 'kill count': */
+               outyx(attacker, STAT_KILL_ROW, STAT_VALUE_COL, "%3d",
+                       (attacker->p_damcap - conf_maxdam) / 2);
+
+               /* Update the attacker's score for everyone else */
+               y = STAT_PLAY_ROW + 1 + (attacker - Player);
+               outyx(ALL_PLAYERS, y, STAT_NAME_COL,
+                       "%5.2f", attacker->p_ident->i_score);
+       }
+}
+
+/*
+ * zap:
+ *     Kill off a player and take them out of the game.
+ *     The 'was_player' flag indicates that the player was not
+ *     a monitor and needs extra cleaning up.
+ */
+static void
+zap(pp, was_player)
+       PLAYER  *pp;
+       FLAG    was_player;
+{
+       int     len;
+       BULLET  *bp;
+       PLAYER  *np;
+       int     x, y;
+       int     savefd;
+
+       if (was_player) {
+               /* If they died from a shot, clean up shrapnel */
+               if (pp->p_undershot)
+                       fixshots(pp->p_y, pp->p_x, pp->p_over);
+               /* Let the player see their last position: */
+               drawplayer(pp, FALSE);
+               /* Remove from game: */
+               Nplayer--;
+       }
+
+       /* Display the cause of death in the centre of the screen: */
+       len = strlen(pp->p_death);
+       x = (WIDTH - len) / 2;
+       outyx(pp, HEIGHT / 2, x, "%s", pp->p_death);
+
+       /* Put some horizontal lines around and below the death message: */
+       memset(pp->p_death + 1, '-', len - 2);
+       pp->p_death[0] = '+';
+       pp->p_death[len - 1] = '+';
+       outyx(pp, HEIGHT / 2 - 1, x, "%s", pp->p_death);
+       outyx(pp, HEIGHT / 2 + 1, x, "%s", pp->p_death);
+
+       /* Move to bottom left */
+       cgoto(pp, HEIGHT, 0);
+
+       savefd = pp->p_fd;
+
+       if (was_player) {
+               int     expl_charge;
+               int     expl_type;
+               int     ammo_exploding;
+
+               /* Check all the bullets: */
+               for (bp = Bullets; bp != NULL; bp = bp->b_next) {
+                       if (bp->b_owner == pp)
+                               /* Zapped players can't own bullets: */
+                               bp->b_owner = NULL;
+                       if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
+                               /* Bullets over the player are now over air: */
+                               bp->b_over = SPACE;
+               }
+
+               /* Explode a random fraction of the player's ammo: */
+               ammo_exploding = rand_num(pp->p_ammo);
+
+               /* Determine the type and amount of detonation: */
+               expl_charge = rand_num(ammo_exploding + 1);
+               if (pp->p_ammo == 0)
+                       /* Ignore the no-ammo case: */
+                       expl_charge = 0;
+               else if (ammo_exploding >= pp->p_ammo - 1) {
+                       /* Maximal explosions always appear as slime: */
+                       expl_charge = pp->p_ammo;
+                       expl_type = SLIME;
+               } else {
+                       /*
+                        * Figure out the best effective explosion
+                        * type to use, given the amount of charge
+                        */
+                       int btype, stype;
+                       for (btype = MAXBOMB - 1; btype > 0; btype--)
+                               if (expl_charge >= shot_req[btype])
+                                       break;
+                       for (stype = MAXSLIME - 1; stype > 0; stype--)
+                               if (expl_charge >= slime_req[stype])
+                                       break;
+                       /* Pick the larger of the bomb or slime: */
+                       if (btype >= 0 && stype >= 0) {
+                               if (shot_req[btype] > slime_req[btype])
+                                       btype = -1;
+                       }
+                       if (btype >= 0)  {
+                               expl_type = shot_type[btype];
+                               expl_charge = shot_req[btype];
+                       } else
+                               expl_type = SLIME;
+               }
+
+               if (expl_charge > 0) {
+                       char buf[BUFSIZ];
+
+                       /* Detonate: */
+                       (void) add_shot(expl_type, pp->p_y, pp->p_x, 
+                           pp->p_face, expl_charge, (PLAYER *) NULL, 
+                           TRUE, SPACE);
+
+                       /* Explain what the explosion is about. */
+                       snprintf(buf, sizeof buf, "%s detonated.", 
+                               pp->p_ident->i_name);
+                       message(ALL_PLAYERS, buf);
+
+                       while (pp->p_nboots-- > 0) {
+                               /* Throw one of the boots away: */
+                               for (np = Boot; np < &Boot[NBOOTS]; np++)
+                                       if (np->p_flying < 0)
+                                               break;
+#ifdef DIAGNOSTIC
+                               if (np >= &Boot[NBOOTS])
+                                       err(1, "Too many boots");
+#endif
+                               /* Start the boots from where the player is */
+                               np->p_undershot = FALSE;
+                               np->p_x = pp->p_x;
+                               np->p_y = pp->p_y;
+                               /* Throw for up to 20 steps */
+                               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);
+                       }
+               }
+               /* No explosion. Leave the player's boots behind. */
+               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]);
+               }
+
+               /* Any unexploded ammo builds up in the volcano: */
+               volcano += pp->p_ammo - expl_charge;
+
+               /* Volcano eruption: */
+               if (conf_volcano && rand_num(100) < volcano / 
+                   conf_volcano_max) {
+                       /* Erupt near the middle of the map */
+                       do {
+                               x = rand_num(WIDTH / 2) + WIDTH / 4;
+                               y = rand_num(HEIGHT / 2) + HEIGHT / 4;
+                       } while (Maze[y][x] != SPACE);
+
+                       /* Convert volcano charge into lava: */
+                       (void) add_shot(LAVA, y, x, LEFTS, volcano,
+                               (PLAYER *) NULL, TRUE, SPACE);
+                       volcano = 0;
+
+                       /* Tell eveyone what's happening */
+                       message(ALL_PLAYERS, "Volcano eruption.");
+               }
+
+               /* Drone: */
+               if (conf_drone && rand_num(100) < 2) {
+                       /* Find a starting place near the middle of the map: */
+                       do {
+                               x = rand_num(WIDTH / 2) + WIDTH / 4;
+                               y = rand_num(HEIGHT / 2) + HEIGHT / 4;
+                       } while (Maze[y][x] != SPACE);
+
+                       /* Start the drone going: */
+                       add_shot(DSHOT, y, x, rand_dir(),
+                               shot_req[conf_mindshot +
+                               rand_num(MAXBOMB - conf_mindshot)],
+                               (PLAYER *) NULL, FALSE, SPACE);
+               }
+
+               /* Tell the zapped player's client to shut down. */
+               sendcom(pp, ENDWIN, ' ');
+               (void) fclose(pp->p_output);
+
+               /* Close up the gap in the Player array: */
+               End_player--;
+               if (pp != End_player) {
+                       /* Move the last player into the gap: */
+                       memcpy(pp, End_player, sizeof *pp);
+                       outyx(ALL_PLAYERS, 
+                               STAT_PLAY_ROW + 1 + (pp - Player), 
+                               STAT_NAME_COL,
+                               "%5.2f%c%-10.10s %c",
+                               pp->p_ident->i_score, stat_char(pp),
+                               pp->p_ident->i_name, pp->p_ident->i_team);
+               }
+
+               /* Erase the last player from the display: */
+               cgoto(ALL_PLAYERS, STAT_PLAY_ROW + 1 + Nplayer, STAT_NAME_COL);
+               ce(ALL_PLAYERS);
+       }
+       else {
+               /* Zap a monitor */
+
+               /* Close the session: */
+               sendcom(pp, ENDWIN, LAST_PLAYER);
+               (void) fclose(pp->p_output);
+
+               /* shuffle the monitor table */
+               End_monitor--;
+               if (pp != End_monitor) {
+                       memcpy(pp, End_monitor, sizeof *pp);
+                       outyx(ALL_PLAYERS, 
+                               STAT_MON_ROW + 1 + (pp - Player), STAT_NAME_COL,
+                               "%5.5s %-10.10s %c", " ",
+                               pp->p_ident->i_name, pp->p_ident->i_team);
+               }
+
+               /* Erase the last monitor in the list */
+               cgoto(ALL_PLAYERS,
+                       STAT_MON_ROW + 1 + (End_monitor - Monitor),
+                       STAT_NAME_COL);
+               ce(ALL_PLAYERS);
+       }
+
+       /* Update the file descriptor sets used by select: */
+       FD_CLR(savefd, &Fds_mask);
+       if (Num_fds == savefd + 1) {
+               Num_fds = Socket;
+               if (Server_socket > Socket)
+                       Num_fds = Server_socket;
+               for (np = Player; np < End_player; np++)
+                       if (np->p_fd > Num_fds)
+                               Num_fds = np->p_fd;
+               for (np = Monitor; np < End_monitor; np++)
+                       if (np->p_fd > Num_fds)
+                               Num_fds = np->p_fd;
+               Num_fds++;
+       }
+}
+
+/*
+ * rand_num:
+ *     Return a random number in a given range.
+ */
+int
+rand_num(range)
+       int     range;
+{
+       if (range == 0)
+               return 0;
+       return (random() % 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.
+ */
+static int
+havechar(pp)
+       PLAYER  *pp;
+{
+       int ret;
+
+       /* Do we already have characters? */
+       if (pp->p_ncount < pp->p_nchar)
+               return TRUE;
+       /* Ignore if nothing to read. */
+       if (!FD_ISSET(pp->p_fd, &Have_inp))
+               return FALSE;
+       /* Remove the player from the read set until we have drained them: */
+       FD_CLR(pp->p_fd, &Have_inp);
+
+       /* Suck their keypresses into a buffer: */
+check_again:
+       errno = 0;
+       ret = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf);
+       if (ret == -1) {
+               if (errno == EINTR)
+                       goto check_again;
+               if (errno == EAGAIN) {
+#ifdef DEBUG
+                       warn("Have_inp is wrong for %d", pp->p_fd);
+#endif
+                       return FALSE;
+               }
+               logit(LOG_INFO, "read");
+       }
+       if (ret > 0) {
+               /* Got some data */
+               pp->p_nchar = ret;
+       } else {
+               /* Connection was lost/closed: */
+               pp->p_cbuf[0] = 'q';
+               pp->p_nchar = 1;
+       }
+       /* Reset pointer into read buffer */
+       pp->p_ncount = 0;
+       return TRUE;
+}
+
+/*
+ * cleanup:
+ *     Exit with the given value, cleaning up any droppings lying around
+ */
+void
+cleanup(eval)
+       int     eval;
+{
+       PLAYER  *pp;
+
+       /* Place their cursor in a friendly position: */
+       cgoto(ALL_PLAYERS, HEIGHT, 0);
+
+       /* Send them all the ENDWIN command: */
+       sendcom(ALL_PLAYERS, ENDWIN, LAST_PLAYER);
+
+       /* And close their connections: */
+       for (pp = Player; pp < End_player; pp++)
+               (void) fclose(pp->p_output);
+       for (pp = Monitor; pp < End_monitor; pp++)
+               (void) fclose(pp->p_output);
+
+       /* Close the server socket: */
+       (void) close(Socket);
+
+       /* The end: */
+       logx(LOG_INFO, "game over");
+       exit(eval);
+}
+
+/*
+ * send_stats:
+ *     Accept a connection to the statistics port, and emit
+ *     the stats.
+ */
+static void
+send_stats()
+{
+       FILE    *fp;
+       int     s;
+       struct sockaddr_in      sockstruct;
+       socklen_t       socklen;
+       struct request_info ri;
+       int     flags;
+
+       /* Accept a connection to the statistics socket: */
+       socklen = sizeof sockstruct;
+       s = accept(Status, (struct sockaddr *) &sockstruct, &socklen);
+       if (s < 0) {
+               if (errno == EINTR)
+                       return;
+               logx(LOG_ERR, "accept");
+               return;
+       }
+
+       /* Check for access permissions: */
+       request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, s, 0);
+       fromhost(&ri);
+       if (hosts_access(&ri) == 0) {
+               logx(LOG_INFO, "rejected connection from %s", eval_client(&ri));
+               close(s);
+               return;
+       }
+
+       /* Don't allow the writes to block: */
+       flags = fcntl(s, F_GETFL, 0);
+       flags |= O_NDELAY;
+       (void) fcntl(s, F_SETFL, flags);
+
+       fp = fdopen(s, "w");
+       if (fp == NULL) {
+               logit(LOG_ERR, "fdopen");
+               (void) close(s);
+               return;
+       }
+
+       print_stats(fp);
+
+       (void) fclose(fp);
+}
+
+/*
+ * print_stats:
+ *     emit the game statistics
+ */
+void
+print_stats(fp)
+       FILE *fp;
+{
+       IDENT   *ip;
+       PLAYER  *pp;
+
+       /* Send the statistics as raw text down the socket: */
+       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%c%c%c\t", ip->i_name,
+                       ip->i_team == ' ' ? ' ' : '[',
+                       ip->i_team,
+                       ip->i_team == ' ' ? ' ' : ']'
+               );
+               if (strlen(ip->i_name) + 3 < 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\tConnect\n", fp);
+       for (ip = Scores; ip != NULL; ip = ip->i_next) {
+               fprintf(fp, "%s%c%c%c\t", ip->i_name,
+                       ip->i_team == ' ' ? ' ' : '[',
+                       ip->i_team,
+                       ip->i_team == ' ' ? ' ' : ']'
+               );
+               if (strlen(ip->i_name) + 3 < 8)
+                       putc('\t', fp);
+               fprintf(fp, "%d\t%d\t%d\t%d\t%d\t",
+                       ip->i_gkills, ip->i_bkills, ip->i_deaths,
+                       ip->i_stillb, ip->i_saved);
+               for (pp = Player; pp < End_player; pp++)
+                       if (pp->p_ident == ip)
+                               putc('p', fp);
+               for (pp = Monitor; pp < End_monitor; pp++)
+                       if (pp->p_ident == ip)
+                               putc('m', fp);
+               putc('\n', fp);
+       }
+}
+
+
+/*
+ * Send the game statistics to the controlling tty
+ */
+static void
+siginfo(int sig __unused)
+{
+       int tty;
+       FILE *fp;
+
+       if ((tty = open(_PATH_TTY, O_WRONLY)) >= 0) {
+               fp = fdopen(tty, "w");
+               print_stats(fp);
+               answer_info(fp);
+               fclose(fp);
+       }
+}
+
+/*
+ * clear_scores:
+ *     Clear the Scores list.
+ */
+static void
+clear_scores()
+{
+       IDENT   *ip, *nextip;
+
+       /* Release the list of scores: */
+       for (ip = Scores; ip != NULL; ip = nextip) {
+               nextip = ip->i_next;
+               free((char *) ip);
+       }
+       Scores = NULL;
+}
+
+/*
+ * announce_game:
+ *     Publically announce the game
+ */
+static void
+announce_game()
+{
+
+       /* TODO: could use system() to do something user-configurable */
+}
+
+/*
+ * Handle a UDP packet sent to the well known port.
+ */
+static void
+handle_wkport(fd)
+       int fd;
+{
+       struct sockaddr         fromaddr;
+       socklen_t               fromlen;
+       u_int16_t               query;
+       u_int16_t               response;
+       struct request_info     ri;
+
+       request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, fd, 0);
+       fromhost(&ri);
+       fromlen = sizeof fromaddr;
+       if (recvfrom(fd, &query, sizeof query, 0, &fromaddr, &fromlen) == -1)
+       {
+               logit(LOG_WARNING, "recvfrom");
+               return;
+       }
+
+#ifdef DEBUG
+       fprintf(stderr, "query %d (%s) from %s:%d\n", query,
+               query == C_MESSAGE ? "C_MESSAGE" :
+               query == C_SCORES ? "C_SCORES" :
+               query == C_PLAYER ? "C_PLAYER" :
+               query == C_MONITOR ? "C_MONITOR" : "?",
+               inet_ntoa(((struct sockaddr_in *)&fromaddr)->sin_addr),
+               ntohs(((struct sockaddr_in *)&fromaddr)->sin_port));
+#endif
+
+       /* Do we allow access? */
+       if (hosts_access(&ri) == 0) {
+               logx(LOG_INFO, "rejected connection from %s", eval_client(&ri));
+               return;
+       }
+
+       query = ntohs(query);
+
+       switch (query) {
+         case C_MESSAGE:
+               if (Nplayer <= 0)
+                       /* Don't bother replying if nobody to talk to: */
+                       return;
+               /* Return the number of people playing: */
+               response = Nplayer;
+               break;
+         case C_SCORES:
+               /* Someone wants the statistics port: */
+               response = stat_port;
+               break;
+         case C_PLAYER:
+         case C_MONITOR:
+               /* Someone wants to play or watch: */
+               if (query == C_MONITOR && Nplayer <= 0)
+                       /* Don't bother replying if there's nothing to watch: */
+                       return;
+               /* Otherwise, tell them how to get to the game: */
+               response = sock_port;
+               break;
+         default:
+               logit(LOG_INFO, "unknown udp query %d", query);
+               return;
+       }
+
+       response = ntohs(response);
+       if (sendto(fd, &response, sizeof response, 0,
+           &fromaddr, sizeof fromaddr) == -1)
+               logit(LOG_WARNING, "sendto");
+}
diff --git a/games/hunt/huntd/execute.c b/games/hunt/huntd/execute.c
new file mode 100644 (file)
index 0000000..1c74aa5
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: execute.c,v 1.8 2004/01/16 00:13:19 espie Exp $
+ * $NetBSD: execute.c,v 1.2 1997/10/10 16:33:13 lukem Exp $
+ * $DragonFly: src/games/hunt/huntd/execute.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "hunt.h"
+#include "conf.h"
+#include "server.h"
+
+static void    cloak(PLAYER *);
+static void    face(PLAYER *, int);
+static void    fire(PLAYER *, int);
+static void    fire_slime(PLAYER *, int);
+static void    move_player(PLAYER *, int);
+static void    pickup(PLAYER *, int, int, int, int);
+static void    scan(PLAYER *);
+
+
+/*
+ * mon_execute:
+ *     Execute a single monitor command
+ */
+void
+mon_execute(pp)
+       PLAYER  *pp;
+{
+       char    ch;
+
+       ch = pp->p_cbuf[pp->p_ncount++];
+
+       switch (ch) {
+         case CTRL('L'):
+               /* Redraw messed-up screen */
+               sendcom(pp, REDRAW);
+               break;
+         case 'q':
+               /* Quit client */
+               (void) strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death);
+               break;
+         default:
+               /* Ignore everything else */
+               ;
+       }
+}
+
+/*
+ * execute:
+ *     Execute a single command from a player
+ */
+void
+execute(pp)
+       PLAYER  *pp;
+{
+       char    ch;
+
+       ch = pp->p_cbuf[pp->p_ncount++];
+
+       /* When flying, only allow refresh and quit. */
+       if (pp->p_flying >= 0) {
+               switch (ch) {
+                 case CTRL('L'):
+                       sendcom(pp, REDRAW);
+                       break;
+                 case 'q':
+                       (void) strlcpy(pp->p_death, "| Quit |", 
+                           sizeof pp->p_death);
+                       break;
+               }
+               return;
+       }
+
+       /* Decode the command character: */
+       switch (ch) {
+         case CTRL('L'):
+               sendcom(pp, REDRAW);    /* Refresh */
+               break;
+         case 'h':
+               move_player(pp, LEFTS); /* Move left */
+               break;
+         case 'H':
+               face(pp, LEFTS);        /* Face left */
+               break;
+         case 'j':
+               move_player(pp, BELOW); /* Move down */
+               break;
+         case 'J':
+               face(pp, BELOW);        /* Face down */
+               break;
+         case 'k':
+               move_player(pp, ABOVE); /* Move up */
+               break;
+         case 'K':
+               face(pp, ABOVE);        /* Face up */
+               break;
+         case 'l':
+               move_player(pp, RIGHT); /* Move right */
+               break;
+         case 'L':
+               face(pp, RIGHT);        /* Face 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;
+         case 'o':
+               fire_slime(pp, 0);      /* SLIME */
+               break;
+         case 'O':
+               fire_slime(pp, 1);      /* SSLIME */
+               break;
+         case 'p':
+               fire_slime(pp, 2);      /* large slime */
+               break;
+         case 'P':
+               fire_slime(pp, 3);      /* very large slime */
+               break;
+         case 's':                     /* start scanning */
+               scan(pp);
+               break;
+         case 'c':                     /* start cloaking */
+               cloak(pp);
+               break;
+         case 'q':                     /* quit */
+               (void) strlcpy(pp->p_death, "| Quit |", sizeof pp->p_death);
+               break;
+       }
+}
+
+/*
+ * move_player:
+ *     Try to move player 'pp' in direction 'dir'.
+ */
+static void
+move_player(pp, dir)
+       PLAYER  *pp;
+       int     dir;
+{
+       PLAYER  *newp;
+       int     x, y;
+       FLAG    moved;
+       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;
+
+       /* What would the player move over: */
+       switch (Maze[y][x]) {
+         /* Players can move through spaces and doors, no problem: */
+         case SPACE:
+         case DOOR:
+               moved = TRUE;
+               break;
+         /* Can't move through walls: */
+         case WALL1:
+         case WALL2:
+         case WALL3:
+         case WALL4:
+         case WALL5:
+               break;
+         /* Moving over a mine - try to pick it up: */
+         case MINE:
+         case GMINE:
+               if (dir == pp->p_face)
+                       /* facing it: 2% chance of trip */
+                       pickup(pp, y, x, conf_ptrip_face, Maze[y][x]);
+               else if (opposite(dir, pp->p_face))
+                       /* facing away: 95% chance of trip */
+                       pickup(pp, y, x, conf_ptrip_back, Maze[y][x]);
+               else
+                       /* facing sideways: 50% chance of trip */
+                       pickup(pp, y, x, conf_ptrip_side, Maze[y][x]);
+               /* Remove the mine: */
+               Maze[y][x] = SPACE;
+               moved = TRUE;
+               break;
+         /* Moving into a bullet: */
+         case SHOT:
+         case GRENADE:
+         case SATCHEL:
+         case BOMB:
+         case SLIME:
+         case DSHOT:
+               /* Find which bullet: */
+               bp = is_bullet(y, x);
+               if (bp != NULL)
+                       /* Detonate it: */
+                       bp->b_expl = TRUE;
+               /* Remove it: */
+               Maze[y][x] = SPACE;
+               moved = TRUE;
+               break;
+         /* Moving into another player: */
+         case LEFTS:
+         case RIGHT:
+         case ABOVE:
+         case BELOW:
+               if (dir != pp->p_face)
+                       /* Can't walk backwards/sideways into another player: */
+                       sendcom(pp, BELL);
+               else {
+                       /* Stab the other player */
+                       newp = play_at(y, x);
+                       checkdam(newp, pp, pp->p_ident, conf_stabdam, KNIFE);
+               }
+               break;
+         /* Moving into a player flying overhead: */
+         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;
+         /* Picking up a boot, or two: */
+         case BOOT_PAIR:
+               pp->p_nboots++;
+         case BOOT:
+               pp->p_nboots++;
+               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;
+       }
+
+       /* Can the player be moved? */
+       if (moved) {
+               /* Check the gun status: */
+               if (pp->p_ncshot > 0)
+                       if (--pp->p_ncshot == conf_maxncshot)
+                               outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, " ok");
+               /* Check for bullets flying past: */
+               if (pp->p_undershot) {
+                       fixshots(pp->p_y, pp->p_x, pp->p_over);
+                       pp->p_undershot = FALSE;
+               }
+               /* Erase the player: */
+               drawplayer(pp, FALSE);
+               /* Save under: */
+               pp->p_over = Maze[y][x];
+               /* Move the player: */
+               pp->p_y = y;
+               pp->p_x = x;
+               /* Draw the player in their new position */
+               drawplayer(pp, TRUE);
+       }
+}
+
+/*
+ * face:
+ *     Change the direction the player is facing
+ */
+static void
+face(pp, dir)
+       PLAYER  *pp;
+       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
+ */
+static void
+fire(pp, req_index)
+       PLAYER  *pp;
+       int     req_index;
+{
+       if (pp == NULL)
+               return;
+
+       /* Drop the shot type down until we can afford it: */
+       while (req_index >= 0 && pp->p_ammo < shot_req[req_index])
+               req_index--;
+
+       /* Can we shoot at all? */
+       if (req_index < 0) {
+               message(pp, "Not enough charges.");
+               return;
+       }
+
+       /* Check if the gun is too hot: */
+       if (pp->p_ncshot > conf_maxncshot)
+               return;
+
+       /* Heat up the gun: */
+       if (pp->p_ncshot++ == conf_maxncshot) {
+               /* The gun has overheated: */
+               outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, "   ");
+       }
+
+       /* Use up some ammo: */
+       pp->p_ammo -= shot_req[req_index];
+       ammo_update(pp);
+
+       /* Start the bullet moving: */
+       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 bullet to everyone: */
+       showexpl(pp->p_y, pp->p_x, shot_type[req_index]);
+       sendcom(ALL_PLAYERS, REFRESH);
+}
+
+/*
+ * fire_slime:
+ *     Fire a slime shot in the given direction
+ */
+static void
+fire_slime(pp, req_index)
+       PLAYER  *pp;
+       int     req_index;
+{
+       if (pp == NULL)
+               return;
+
+       /* Check configuration: */
+       if (!conf_ooze)
+               return;
+
+       /* Drop the slime type back util we can afford it: */
+       while (req_index >= 0 && pp->p_ammo < slime_req[req_index])
+               req_index--;
+
+       /* Can we afford to slime at all? */
+       if (req_index < 0) {
+               message(pp, "Not enough charges.");
+               return;
+       }
+
+       /* Is the gun too hot? */
+       if (pp->p_ncshot > conf_maxncshot)
+               return;
+
+       /* Heat up the gun: */
+       if (pp->p_ncshot++ == conf_maxncshot) {
+               /* The gun has overheated: */
+               outyx(pp, STAT_GUN_ROW, STAT_VALUE_COL, "   ");
+       }
+
+       /* Use up some ammo: */
+       pp->p_ammo -= slime_req[req_index];
+       ammo_update(pp);
+
+       /* Start the slime moving: */
+       add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face,
+               slime_req[req_index] * conf_slimefactor, pp, FALSE, pp->p_face);
+       pp->p_undershot = TRUE;
+
+       /* Show the object to everyone: */
+       showexpl(pp->p_y, pp->p_x, SLIME);
+       sendcom(ALL_PLAYERS, REFRESH);
+}
+
+/*
+ * add_shot:
+ *     Create a shot with the given properties
+ */
+void
+add_shot(type, y, x, wface, charge, owner, expl, over)
+       int     type;
+       int     y, x;
+       char    wface;
+       int     charge;
+       PLAYER  *owner;
+       int     expl;
+       char    over;
+{
+       BULLET  *bp;
+       int     size;
+
+       /* Determine the bullet's size based on its type and charge: */
+       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;
+       }
+
+       /* Create the bullet: */
+       bp = create_shot(type, y, x, wface, charge, size, owner,
+               (owner == NULL) ? NULL : owner->p_ident, expl, over);
+
+       /* Insert the bullet into the front of the bullet list: */
+       bp->b_next = Bullets;
+       Bullets = bp;
+}
+
+/*
+ * create_shot:
+ *     allocate storage for an (unlinked) bullet structure;
+ *     initialize and return it
+ */
+BULLET *
+create_shot(type, y, x, wface, charge, size, owner, score, expl, over)
+       int     type;
+       int     y, x;
+       char    wface;
+       int     charge;
+       int     size;
+       PLAYER  *owner;
+       IDENT   *score;
+       int     expl;
+       char    over;
+{
+       BULLET  *bp;
+
+       bp = (BULLET *) malloc(sizeof (BULLET));        /* NOSTRICT */
+       if (bp == NULL) {
+               logit(LOG_ERR, "malloc");
+               if (owner != NULL)
+                       message(owner, "Out of memory");
+               return NULL;
+       }
+
+       bp->b_face = wface;
+       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
+ */
+static void
+cloak(pp)
+       PLAYER  *pp;
+{
+       /* Check configuration: */
+       if (!conf_cloak)
+               return;
+
+       /* Can we afford it?: */
+       if (pp->p_ammo <= 0) {
+               message(pp, "No more charges");
+               return;
+       }
+
+       /* Can't cloak with boots: */
+       if (pp->p_nboots > 0) {
+               message(pp, "Boots are too noisy to cloak!");
+               return;
+       }
+
+       /* Consume a unit of ammo: */
+       pp->p_ammo--;
+       ammo_update(pp);
+
+       /* Add to the duration of a cloak: */
+       pp->p_cloak += conf_cloaklen;
+
+       /* Disable scan, if enabled: */
+       if (pp->p_scan >= 0)
+               pp->p_scan = -1;
+
+       /* Re-draw the player's scan/cloak status: */
+       showstat(pp);
+}
+
+/*
+ * scan:
+ *     Turn on or increase length of a scan
+ */
+static void
+scan(pp)
+       PLAYER  *pp;
+{
+       /* Check configuration: */
+       if (!conf_scan)
+               return;
+
+       /* Can we afford it?: */
+       if (pp->p_ammo <= 0) {
+               message(pp, "No more charges");
+               return;
+       }
+
+       /* Consume one unit of ammo: */
+       pp->p_ammo--;
+       ammo_update(pp);
+
+       /* Increase the scan time: */
+       pp->p_scan += Nplayer * conf_scanlen;
+
+       /* Disable cloak, if enabled: */
+       if (pp->p_cloak >= 0)
+               pp->p_cloak = -1;
+
+       /* Re-draw the player's scan/cloak status: */
+       showstat(pp);
+}
+
+/*
+ * pickup:
+ *     pick up a mine or grenade, with some probability of it exploding
+ */
+static void
+pickup(pp, y, x, prob, obj)
+       PLAYER  *pp;
+       int     y, x;
+       int     prob;
+       int     obj;
+{
+       int     req;
+
+       /* Figure out how much ammo the player is trying to pick up: */
+       switch (obj) {
+         case MINE:
+               req = BULREQ;
+               break;
+         case GMINE:
+               req = GRENREQ;
+               break;
+         default:
+#ifdef DIAGNOSTIC
+               abort();
+#endif
+               return;
+       }
+
+       /* Does it explode? */
+       if (rand_num(100) < prob)
+               /* Ooooh, unlucky: (Boom) */
+               add_shot(obj, y, x, LEFTS, req, (PLAYER *) NULL,
+                       TRUE, pp->p_face);
+       else {
+               /* Safely picked it up. Add to player's ammo: */
+               pp->p_ammo += req;
+               ammo_update(pp);
+       }
+}
+
+void
+ammo_update(pp)
+       PLAYER *pp;
+{
+       outyx(pp, STAT_AMMO_ROW, STAT_VALUE_COL - 1, "%4d", pp->p_ammo);
+}
diff --git a/games/hunt/huntd/expl.c b/games/hunt/huntd/expl.c
new file mode 100644 (file)
index 0000000..f2ab0b7
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: expl.c,v 1.9 2007/09/04 22:39:31 hshoexer Exp $
+ * $NetBSD: expl.c,v 1.2 1997/10/10 16:33:18 lukem Exp $
+ * $DragonFly: src/games/hunt/huntd/expl.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include "hunt.h"
+#include "server.h"
+#include "conf.h"
+
+static void    remove_wall(int, int);
+static void    init_removed(void);
+
+
+/*
+ * showexpl:
+ *     Show the explosions as they currently are
+ */
+void
+showexpl(y, x, type)
+       int     y, x;
+       char    type;
+{
+       PLAYER  *pp;
+       EXPL    *ep;
+
+       if (y < 0 || y >= HEIGHT)
+               return;
+       if (x < 0 || x >= WIDTH)
+               return;
+       ep = (EXPL *) malloc(sizeof (EXPL));    /* NOSTRICT */
+       if (ep == NULL) {
+               logit(LOG_ERR, "malloc");
+               return;
+       }
+       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);
+       }
+       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);
+       }
+       switch (Maze[y][x]) {
+         case WALL1:
+         case WALL2:
+         case WALL3:
+         case DOOR:
+         case WALL4:
+         case WALL5:
+               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
+ */
+void
+rollexpl()
+{
+       EXPL    *ep;
+       PLAYER  *pp;
+       int     y, x;
+       char    c;
+       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);
+                       }
+               for (pp = Monitor; pp < End_monitor; pp++)
+                       check(pp, y, x);
+               free((char *) ep);
+       }
+       memmove(&Expl[1], &Expl[0], (EXPLEN - 1) * sizeof Expl[0]);
+       /* for (x = EXPLEN - 1; x > 0; x--)
+               Expl[x] = Expl[x - 1]; */
+       Last_expl = Expl[0] = NULL;
+}
+
+int
+can_rollexpl()
+{
+       int i;
+
+       for (i = EXPLEN - 1; i >= 0; i--)
+               if (Expl[i] != NULL)
+                       return 1;
+       return 0;
+}
+
+static REGEN   *removed = NULL;
+static REGEN   *rem_index = NULL;
+
+static void
+init_removed()
+{
+       rem_index = removed = calloc(conf_maxremove, sizeof(REGEN));
+       if (rem_index == NULL) {
+               logit(LOG_ERR, "malloc");
+               cleanup(1);
+       }
+}
+
+/*
+ * 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.
+ */
+static void
+remove_wall(y, x)
+       int     y, x;
+{
+       REGEN   *r;
+       PLAYER  *pp;
+       char    save_char = 0;
+
+       if (removed == NULL)
+               clearwalls();
+
+       r = rem_index;
+       while (r->r_y != 0) {
+               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;
+               }
+               if (++r >= removed + conf_maxremove)
+                       r = removed;
+       }
+
+found:
+       if (r->r_y != 0) {
+               /* Slot being used, put back this wall */
+               if (save_char == SPACE)
+                       Maze[r->r_y][r->r_x] = Orig_maze[r->r_y][r->r_x];
+               else {
+                       /* We throw the player off the wall: */
+                       pp = play_at(r->r_y, r->r_x);
+                       if (pp->p_flying >= 0)
+                               pp->p_flying += rand_num(conf_flytime / 2);
+                       else {
+                               pp->p_flying = rand_num(conf_flytime);
+                               pp->p_flyx = 2 * rand_num(conf_flystep + 1) -
+                                   conf_flystep;
+                               pp->p_flyy = 2 * rand_num(conf_flystep + 1) -
+                                   conf_flystep;
+                       }
+                       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);
+               }
+               Maze[r->r_y][r->r_x] = Orig_maze[r->r_y][r->r_x];
+               if (conf_random && rand_num(100) < conf_prandom)
+                       Maze[r->r_y][r->r_x] = DOOR;
+               if (conf_reflect && rand_num(100) == conf_preflect)
+                       Maze[r->r_y][r->r_x] = WALL4;
+               for (pp = Monitor; pp < End_monitor; pp++)
+                       check(pp, r->r_y, r->r_x);
+       }
+
+       r->r_y = y;
+       r->r_x = x;
+       if (++r >= removed + conf_maxremove)
+               rem_index = removed;
+       else
+               rem_index = r;
+
+       Maze[y][x] = SPACE;
+       for (pp = Monitor; pp < End_monitor; pp++)
+               check(pp, y, x);
+}
+
+/*
+ * clearwalls:
+ *     Clear out the walls array
+ */
+void
+clearwalls()
+{
+       REGEN   *rp;
+
+       if (removed == NULL)
+               init_removed();
+       for (rp = removed; rp < removed + conf_maxremove; rp++)
+               rp->r_y = 0;
+       rem_index = removed;
+}
diff --git a/games/hunt/huntd/extern.c b/games/hunt/huntd/extern.c
new file mode 100644 (file)
index 0000000..d8f6d3f
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: extern.c,v 1.4 2003/06/11 08:45:33 pjanzen Exp $
+ * $NetBSD: extern.c,v 1.2 1997/10/10 16:33:24 lukem Exp $
+ * $DragonFly: src/games/hunt/huntd/extern.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+# include      "hunt.h"
+# include      "server.h"
+
+FLAG   Am_monitor = FALSE;             /* current process is a monitor */
+
+char   Buf[BUFSIZ];                    /* general scribbling buffer */
+char   Maze[HEIGHT][WIDTH2];           /* the maze */
+char   Orig_maze[HEIGHT][WIDTH2];      /* the original maze */
+
+fd_set Fds_mask;                       /* mask for the file descriptors */
+fd_set 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 */
+int    Status;                         /* 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 */
+PLAYER Boot[NBOOTS];                   /* all the boots */
+IDENT  *Scores;                        /* score cache */
+PLAYER Monitor[MAXMON];                /* all the monitors */
+PLAYER *End_monitor = Monitor;         /* last active monitor slot */
+
+int    volcano = 0;                    /* Explosion size */
+
+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/games/hunt/huntd/hunt.h b/games/hunt/huntd/hunt.h
new file mode 100644 (file)
index 0000000..f1c59ea
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: hunt.h,v 1.5 2003/06/11 08:45:33 pjanzen Exp $
+ * $NetBSD: hunt.h,v 1.5 1998/09/13 15:27:28 hubertf Exp $
+ * $DragonFly: src/games/hunt/huntd/hunt.h,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+/*
+ * Preprocessor define dependencies
+ */
+
+/* decrement version number for each change in startup protocol */
+# define       HUNT_VERSION            (-1)
+# define       HUNT_PORT               (('h' << 8) | 't')
+
+# 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)
+
+# define       SCREEN_HEIGHT   24
+# define       SCREEN_WIDTH    80
+# define       HEIGHT  23
+# define       WIDTH   51
+# define       SCREEN_WIDTH2   128     /* Next power of 2 >= SCREEN_WIDTH */
+# define       WIDTH2  64      /* Next power of 2 >= WIDTH (for fast access) */
+
+# define       NAMELEN         20
+
+# 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
+# define       C_TESTMSG()     (Query_driver ? C_MESSAGE :\
+                               (Show_scores ? C_SCORES :\
+                               (Am_monitor ? C_MONITOR :\
+                               C_PLAYER)))
+
+typedef int                    FLAG;
+
+/* Objects within the maze: */
+
+# define       DOOR    '#'
+# define       WALL1   '-'
+# define       WALL2   '|'
+# define       WALL3   '+'
+# define       WALL4   '/'
+# define       WALL5   '\\'
+# define       KNIFE   'K'
+# define       SHOT    ':'
+# define       GRENADE 'o'
+# define       SATCHEL 'O'
+# define       BOMB    '@'
+# define       MINE    ';'
+# define       GMINE   'g'
+# define       SLIME   '$'
+# define       LAVA    '~'
+# define       DSHOT   '?'
+# define       FALL    'F'
+# define       BOOT            'b'
+# define       BOOT_PAIR       'B'
+
+# define       SPACE   ' '
+
+# define       ABOVE   'i'
+# define       BELOW   '!'
+# define       RIGHT   '}'
+# define       LEFTS   '{'
+# define       FLYER   '&'
+# define       is_player(c)    (c == LEFTS || c == RIGHT ||\
+                               c == ABOVE || c == BELOW || c == FLYER)
+
+# ifndef TRUE
+# define       TRUE    1
+# define       FALSE   0
+# endif
+
diff --git a/games/hunt/huntd/huntd.6 b/games/hunt/huntd/huntd.6
new file mode 100644 (file)
index 0000000..e5dd238
--- /dev/null
@@ -0,0 +1,280 @@
+.\"    $NetBSD: huntd.6,v 1.3 1998/01/09 08:03:42 perry Exp $
+.\"    $OpenBSD: huntd.6,v 1.18 2007/05/31 19:19:18 jmc Exp $
+.\"    $DragonFly: src/games/hunt/huntd/huntd.6,v 1.1 2008/09/02 21:50:21 dillon Exp $
+.\"
+.\"  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.
+.\"
+.Dd $Mdocdate$
+.Dt HUNTD 6
+.Os
+.Sh NAME
+.Nm huntd
+.Nd hunt daemon, back-end for hunt game
+.Sh SYNOPSIS
+.Nm huntd
+.Op Fl s
+.Op Fl a Ar addr
+.Sm off
+.Oo
+.Fl D Ar var No = Ar value \ \&...
+.Oc
+.Sm on
+.Op Fl p Ar port
+.Sh DESCRIPTION
+.Nm
+controls the multi-player
+.Xr hunt 6
+game.
+.Pp
+The
+.Fl a Ar addr
+option is used to cause the server to listen only on a
+specific interface address.
+The
+.Ar addr
+argument must be given as an IP address.
+.Pp
+Options given with
+.Fl D
+override those read from configuration files
+.Po
+see
+.Sx CONFIGURATION ,
+below
+.Pc .
+.Pp
+The
+.Fl p Ar port
+option changes the UDP port number used to rendezvous with the player
+process and thus allows for private games of
+.Xr hunt 6 .
+.Pp
+The
+.Fl s
+option is for running
+.Nm
+forever
+.Pq server mode .
+This is similar to running it under the control of
+.Xr inetd 8
+.Pq see below ,
+but it consumes a process table entry when no one is playing,
+and monitor clients are not disconnected.
+.Ss INETD
+To run
+.Nm
+from
+.Xr inetd 8 ,
+you'll need to
+add this line to
+.Pa /etc/inetd.conf :
+.Pp
+.Dl hunt dgram udp wait nobody /usr/games/huntd HUNT
+.Pp
+Do not use any of the command line options \(em if you want
+.Xr inetd 8
+to start up
+.Nm huntd
+on a private port, change the port listed in
+.Pa /etc/services .
+.Ss "NETWORK RENDEZVOUS"
+When
+.Xr hunt 6
+starts up, it broadcasts on attached networks,
+using the broadcast or point-to-point destination address for each interface,
+to find a
+.Nm hunt
+game in progress.
+If a
+.Nm huntd
+hears the request, it sends back the port number for the
+.Nm hunt
+process to connect to.
+.Pp
+Regardless of how
+.Nm
+is started, it always checks incoming connections with
+.Xr hosts_access 5 ,
+using a service name of
+.Sq huntd .
+.Ss "CONFIGURATION"
+When
+.Nm
+starts, it looks for configuration files that determine
+game parameters.
+Each line of a configuration file is of the form
+.Ar var No = Ar value .
+Comments start with a hash sign
+.Pq Sq # .
+The configuration files loaded in order
+.Pq if they exist
+are:
+.Pa /etc/hunt.conf ,
+.Pa "$HOME/.hunt.conf" ,
+and
+.Pa ./.hunt.conf .
+.Pp
+Many of these variables require intimate knowledge of the
+driver source code.
+The complete list of configurable variables is as follows.
+.Pp
+.Bl -tag -width pdroneabsorb -compact
+.It Va random
+enable dispersion doors
+.Pq default 1
+.It Va reflect
+enable generation of reflection walls
+.Pq default 1
+.It Va monitor
+enable monitors
+.Pq default 1
+.It Va ooze
+enable slime shots
+.Pq default 1
+.It Va fly
+enable flight
+.Pq default 1
+.It Va volcano
+enable volcanoes
+.Pq default 1
+.It Va drone
+enable drone
+.Pq default 1
+.It Va boots
+enable boots
+.Pq default 1
+.It Va scan
+enable scanning
+.Pq default 1
+.It Va cloak
+enable cloaking
+.Pq default 1
+.It Va logerr
+errors to stderr
+.Pq default 1
+.It Va syslog
+errors to
+.Xr syslogd 8
+.Pq default 0
+.It Va scoredecay
+nr deaths before nr kills begins to decay
+.Pq default 15
+.It Va maxremove
+Maximum number of holes in the maze wall
+.Pq default 40
+.It Va linger
+Seconds to keep game open with no players. \&-1 means forever
+.Pq default 90
+.It Va flytime
+max time flying
+.Pq default 20
+.It Va flystep
+max displacement each flying time unit
+.Pq default 5
+.It Va volcano_max
+max size of volcano
+.Pq default 50
+.It Va ptrip_face
+percentage chance of tripping a grenade on pickup
+.Pq default 2
+.It Va ptrip_back
+percentage chance of same when backing onto it
+.Pq default 95
+.It Va ptrip_side
+percentage chance of same when walking sideways into it
+.Pq default 50
+.It Va prandom
+percentage of time dispersion doors appear
+.Pq default 1
+.It Va preflect
+percentage of time reflection walls appear
+.Pq default 1
+.It Va pshot_coll
+percentage chance of shots colliding
+.Pq default 5
+.It Va pgren_coll
+percentage chance of grenades colliding
+.Pq default 10
+.It Va pgren_catch
+facing player chance of catching grenade
+.Pq default 10
+.It Va pmiss
+percentage chance of bullet missing player
+.Pq default 5
+.It Va pdroneabsorb
+percentage chance of absorbing a drone
+.Pq default 1
+.It Va fall_frac
+divisor of damage used for fall damage
+.Pq default 5
+.It Va bulspd
+speed of bullets
+.Pq default 5
+.It Va ishots
+initial ammo for player
+.Pq default 15
+.It Va nshots
+ammo boost for all when new player joins
+.Pq default 5
+.It Va maxncshot
+max number of simultaneous shots per player
+.Pq default 2
+.It Va maxdam
+the initial shield for each player
+.Pq default 10
+.It Va mindam
+minimum damage from one unit of ammo
+.Pq default 5
+.It Va stabdam
+damage from stabbing
+.Pq default 2
+.It Va killgain
+shield gained from killing someone
+.Pq default 2
+.It Va slimefactor
+charge multiplier for slime
+.Pq default 3
+.It Va slimespeed
+speed of slime
+.Pq default 5
+.It Va lavaspeed
+speed of volcano lava
+.Pq default 1
+.It Va cloaklen
+duration of a cloak
+.Pq default 20
+.It Va scanlen
+duration of a scan
+.Pq default 20
+.It Va mindshot
+minimum shot class needed to make a drone
+.Pq default 2
+.It Va simstep
+maximum simulation step in microseconds.
+Zero means traditional blocking behaviour.
+Try 55000 for something reasonable
+.Pq default 0
+.El
+.Sh "FILES"
+.Bl -tag -width Pa -compact
+.It Pa /etc/hunt.conf
+.It Pa "$HOME/.hunt.conf"
+.It Pa ./.hunt.conf
+.El
+.Sh SEE ALSO
+.Xr hosts_options 5 ,
+.Xr hunt 6 ,
+.Xr inetd 8
+.Sh AUTHORS
+Conrad Huang, Ken Arnold, and Greg Couch;
+.br
+University of California, San Francisco, Computer Graphics Lab
+.Pp
+David Leonard tidied up, and added the configuration file.
+.\"Sh BUGS
diff --git a/games/hunt/huntd/makemaze.c b/games/hunt/huntd/makemaze.c
new file mode 100644 (file)
index 0000000..5f9dd00
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: makemaze.c,v 1.6 2003/06/11 08:45:33 pjanzen Exp $
+ * $NetBSD: makemaze.c,v 1.2 1997/10/10 16:33:43 lukem Exp $
+ * $DragonFly: src/games/hunt/huntd/makemaze.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+#include <string.h>
+
+#include "hunt.h"
+#include "server.h"
+#include "conf.h"
+
+# define       ISCLEAR(y,x)    (Maze[y][x] == SPACE)
+# define       ODD(n)          ((n) & 01)
+
+static int     candig(int, int);
+static void    dig(int, int);
+static void    dig_maze(int, int);
+static void    remap(void);
+
+void
+makemaze()
+{
+       char    *sp;
+       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}
+       };
+
+static void
+dig(y, x)
+       int     y, x;
+{
+       int     *dp;
+       int     *ip;
+       int     ny, nx;
+       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?
+ */
+static int
+candig(y, x)
+       int     y, x;
+{
+       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 */
+}
+
+static void
+dig_maze(x, y)
+       int     x, y;
+{
+       int     tx, ty;
+       int     i, j;
+       int     order[4];
+#define        MNORTH  0x1
+#define        MSOUTH  0x2
+#define        MEAST   0x4
+#define        MWEST   0x8
+
+       tx = ty = 0;
+       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);
+       }
+}
+
+static void
+remap()
+{
+       int     y, x;
+       char    *sp;
+       int     stat;
+
+       for (y = 0; y < HEIGHT; y++)
+               for (x = 0; x < WIDTH; x++) {
+                       sp = &Maze[y][x];
+                       if (*sp == SPACE)
+                               continue;
+                       /* Find occupied adjacent cells. */
+                       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:
+                               if (conf_random)
+                                       *sp = DOOR;
+                               if (conf_reflect)
+                                       *sp = rand_num(2) ? WALL4 : WALL5;
+                               break;
+                         default:
+                               *sp = WALL3;                       /* + */
+                               break;
+                       }
+               }
+       memcpy(Orig_maze, Maze, sizeof Orig_maze);
+}
diff --git a/games/hunt/huntd/server.h b/games/hunt/huntd/server.h
new file mode 100644 (file)
index 0000000..7dcd997
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: server.h,v 1.9 2004/01/16 00:13:19 espie Exp $
+ * $NetBSD: hunt.h,v 1.5 1998/09/13 15:27:28 hubertf Exp $
+ * $DragonFly: src/games/hunt/huntd/server.h,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+
+#include <stdio.h>
+#include <sys/socket.h>
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
+/*
+ * 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).
+ */
+#define MAXPL          14
+#define MAXMON         2
+#if (MAXPL + MAXMON > 16)
+#warning "MAXPL + MAXMON is excessive"
+#endif
+
+#define MSGLEN         SCREEN_WIDTH
+
+#define UBOUND         1
+#define DBOUND         (HEIGHT - 1)
+#define LBOUND         1
+#define RBOUND         (WIDTH - 1)
+
+#define NASCII         128
+
+/* Layout of the scoreboard: */
+#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
+#define STAT_MON_ROW   (STAT_PLAY_ROW + MAXPL + 1)
+#define STAT_NAME_LEN  18
+
+/* Number of boots: */
+#define NBOOTS         2
+
+/* Bitmask of directions */
+#define NORTH          01
+#define SOUTH          02
+#define EAST           010
+#define WEST           020
+
+# undef CTRL
+#define CTRL(x)        ((x) & 037)
+
+#define BULREQ         1               /* 0 */
+#define GRENREQ                9               /* 1 */
+#define SATREQ         25              /* 2 */
+#define BOMB7REQ       49              /* 3 */
+#define BOMB9REQ       81              /* 4 */
+#define BOMB11REQ      121             /* 5 */
+#define BOMB13REQ      169             /* 6 */
+#define BOMB15REQ      225             /* 7 */
+#define BOMB17REQ      289             /* 8 */
+#define BOMB19REQ      361             /* 9 */
+#define BOMB21REQ      441             /* 10 */
+#define MAXBOMB                                   11
+
+#define SLIMEREQ       5               /* 0 */
+#define SSLIMEREQ      10              /* 1 */
+#define SLIME2REQ      15              /* 2 */
+#define SLIME3REQ      20              /* 3 */
+#define MAXSLIME                          4
+
+#define EXPLEN         16
+
+#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)
+
+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;
+
+#define        ALL_PLAYERS             ((PLAYER *)1)
+
+struct ident_def {
+       char    i_name[NAMELEN];
+       char    i_team;
+       u_int32_t i_machine;
+       uid_t   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;
+       int     p_flying;
+       int     p_flyx, p_flyy;
+       int     p_nboots;
+       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;
+};
+
+struct spawn {
+       int             fd;
+       int             reading_msg;
+       struct sockaddr source;
+       socklen_t       sourcelen;
+       uid_t           uid;
+       char            name[NAMELEN+1];
+       u_int8_t        team;
+       u_int32_t       enter_status;
+       char            ttyname[NAMELEN];
+       u_int32_t       mode;
+       char            msg[BUFSIZ];
+       int             msglen;
+       struct spawn *  next;
+       struct spawn ** prevnext;
+       int             inlen;
+       char            inbuf[ sizeof (u_int32_t) + NAMELEN +
+                              sizeof (u_int8_t) + sizeof (u_int32_t) +
+                              NAMELEN + sizeof (u_int32_t) ];
+};
+
+extern struct spawn *  Spawn;
+
+extern int     Socket;
+
+/* answer.c */
+void   answer_first(void);
+int    answer_next(struct spawn *);
+int    rand_dir(void);
+void   answer_info(FILE *);
+
+/* draw.c */
+void   drawmaze(PLAYER *);
+void   look(PLAYER *);
+void   check(PLAYER *, int, int);
+void   showstat(PLAYER *);
+void   drawplayer(PLAYER *, FLAG);
+void   message(PLAYER *, const char *);
+
+/* driver.c */
+int    rand_num(int);
+void   checkdam(PLAYER *, PLAYER *, IDENT *, int, char);
+void   cleanup(int);
+
+/* execute.c */
+void   mon_execute(PLAYER *);
+void   execute(PLAYER *);
+void   add_shot(int, int, int, char, int, PLAYER *, int, char);
+BULLET *create_shot(int, int, int, char, int, int, PLAYER *, IDENT *,
+           int, char);
+void   ammo_update(PLAYER *);
+
+/* expl.c */
+void   showexpl(int, int, char);
+void   rollexpl(void);
+void   clearwalls(void);
+int    can_rollexpl(void);
+
+/* makemaze.c */
+void   makemaze(void);
+
+/* shots.c */
+int    can_moveshots(void);
+void   moveshots(void);
+PLAYER *play_at(int, int);
+int    opposite(int, char);
+BULLET *is_bullet(int, int);
+void   fixshots(int, int, char);
+
+/* terminal.c */
+void   cgoto(PLAYER *, int, int);
+void   outch(PLAYER *, char);
+void   outstr(PLAYER *, const char *, int);
+void   outyx(PLAYER *, int, int, const char *, ...)
+                       __attribute__((format (printf, 4, 5)));
+void   clrscr(PLAYER *);
+void   ce(PLAYER *);
+void   sendcom(PLAYER *, int, ...);
+void   flush(PLAYER *);
+void   logit(int, const char *, ...)
+                       __attribute__((format (printf, 2, 3)));
+void   logx(int, const char *, ...)
+                       __attribute__((format (printf, 2, 3)));
+
+/* extern.c */
+extern FLAG    Am_monitor;
+extern char    Buf[BUFSIZ];
+extern char    Maze[HEIGHT][WIDTH2];
+extern char    Orig_maze[HEIGHT][WIDTH2];
+extern fd_set  Fds_mask;
+extern fd_set  Have_inp;
+extern int     Nplayer;
+extern int     Num_fds;
+extern int     Status;
+extern int     See_over[NASCII];
+extern BULLET *        Bullets;
+extern EXPL *  Expl[EXPLEN];
+extern EXPL *  Last_expl;
+extern PLAYER  Player[MAXPL];
+extern PLAYER *        End_player;
+extern PLAYER  Boot[NBOOTS];
+extern IDENT * Scores;
+extern PLAYER  Monitor[MAXMON];
+extern PLAYER *        End_monitor;
+extern int     volcano;
+extern int     shot_req[MAXBOMB];
+extern int     shot_type[MAXBOMB];
+extern int     slime_req[MAXSLIME];
diff --git a/games/hunt/huntd/shots.c b/games/hunt/huntd/shots.c
new file mode 100644 (file)
index 0000000..1448dac
--- /dev/null
@@ -0,0 +1,1246 @@
+/*
+ * Copyright (c) 1983-2003, Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are 
+ * met:
+ * 
+ * + Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * + Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in the 
+ *   documentation and/or other materials provided with the distribution.
+ * + Neither the name of the University of California, San Francisco nor 
+ *   the names of its contributors may be used to endorse or promote 
+ *   products derived from this software without specific prior written 
+ *   permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $OpenBSD: shots.c,v 1.9 2006/03/27 00:10:15 tedu Exp $
+ * $NetBSD: shots.c,v 1.3 1997/10/11 08:13:50 lukem Exp $
+ * $DragonFly: src/games/hunt/huntd/shots.c,v 1.1 2008/09/02 21:50:21 dillon Exp $
+ */
+
+#include <err.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "hunt.h"
+#include "conf.h"
+#include "server.h"
+
+#define        PLUS_DELTA(x, max)      if (x < max) x++; else x--
+#define        MINUS_DELTA(x, min)     if (x > min) x--; else x++
+
+static void    chkshot(BULLET *, BULLET *);
+static void    chkslime(BULLET *, BULLET *);
+static void    explshot(BULLET *, int, int);
+static void    find_under(BULLET *, BULLET *);
+static int     iswall(int, int);
+static void    mark_boot(BULLET *);
+static void    mark_player(BULLET *);
+static int     move_drone(BULLET *);
+static void    move_flyer(PLAYER *);
+static int     move_normal_shot(BULLET *);
+static void    move_slime(BULLET *, int, BULLET *);
+static void    save_bullet(BULLET *);
+static void    zapshot(BULLET *, BULLET *);
+
+/* Return true if there is pending activity */
+int
+can_moveshots()
+{
+       PLAYER *pp;
+
+       /* Bullets are moving? */
+       if (Bullets)
+               return 1;
+
+       /* Explosions are happening? */
+       if (can_rollexpl())
+               return 1;
+
+       /* Things are flying? */
+       for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
+               if (pp->p_flying >= 0)
+                       return 1;
+       for (pp = Player; pp < End_player; pp++)
+               if (pp->p_flying >= 0)
+                       return 1;
+
+       /* Everything is quiet: */
+       return 0;
+}
+
+/*
+ * moveshots:
+ *     Move the shots already in the air, taking explosions into account
+ */
+void
+moveshots()
+{
+       BULLET  *bp, *next;
+       PLAYER  *pp;
+       int     x, y;
+       BULLET  *blist;
+
+       rollexpl();
+       if (Bullets == NULL)
+               goto no_bullets;
+
+       /*
+        * First we move through the bullet list conf_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.
+        */
+
+       /* Move the list to a working list */
+       blist = Bullets;
+       Bullets = NULL;
+
+       /* Work with bullets on the working list (blist) */
+       for (bp = blist; bp != NULL; bp = next) {
+               next = bp->b_next;
+
+               x = bp->b_x;
+               y = bp->b_y;
+
+               /* Un-draw the bullet on all screens: */
+               Maze[y][x] = bp->b_over;
+               check(ALL_PLAYERS, y, x);
+
+               /* Decide how to move the bullet: */
+               switch (bp->b_type) {
+
+                 /* Normal, atomic bullets: */
+                 case SHOT:
+                 case GRENADE:
+                 case SATCHEL:
+                 case BOMB:
+                       if (move_normal_shot(bp)) {
+                               /* Still there: put back on the active list */
+                               bp->b_next = Bullets;
+                               Bullets = bp;
+                       }
+                       break;
+
+                 /* Slime bullets that explode into slime on impact: */
+                 case SLIME:
+                       if (bp->b_expl || move_normal_shot(bp)) {
+                               /* Still there: put back on the active list */
+                               bp->b_next = Bullets;
+                               Bullets = bp;
+                       }
+                       break;
+
+                 /* Drones that wander about: */
+                 case DSHOT:
+                       if (move_drone(bp)) {
+                               /* Still there: put back on the active list */
+                               bp->b_next = Bullets;
+                               Bullets = bp;
+                       }
+                       break;
+
+                 /* Other/unknown: */
+                 default:
+                       /* Place it back on the active list: */
+                       bp->b_next = Bullets;
+                       Bullets = bp;
+                       break;
+               }
+       }
+
+       /* Again, hang the Bullets list off `blist' and work with that: */
+       blist = Bullets;
+       Bullets = NULL;
+       for (bp = blist; bp != NULL; bp = next) {
+               next = bp->b_next;
+               /* Is the bullet exploding? */
+               if (!bp->b_expl) {
+                       /*
+                        * Its still flying through the air.
+                        * Put it back on the bullet list.
+                        */
+                       save_bullet(bp);
+
+                       /* All the monitors can see the bullet: */
+                       for (pp = Monitor; pp < End_monitor; pp++)
+                               check(pp, bp->b_y, bp->b_x);
+
+                       /* All the scanning players can see the 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);
+               } else {
+                       /* It is exploding. Check what we hit: */
+                       chkshot(bp, next);
+                       /* Release storage for the destroyed bullet: */
+                       free(bp);
+               }
+       }
+
+       /* Re-draw all the players: (in case a bullet wiped them out) */
+       for (pp = Player; pp < End_player; pp++)
+               Maze[pp->p_y][pp->p_x] = pp->p_face;
+
+no_bullets:
+
+       /* Move flying boots through the air: */
+       for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
+               if (pp->p_flying >= 0)
+                       move_flyer(pp);
+
+       /* Move flying players through the air: */
+       for (pp = Player; pp < End_player; pp++) {
+               if (pp->p_flying >= 0)
+                       move_flyer(pp);
+               /* Flush out the explosions: */
+               sendcom(pp, REFRESH);
+               look(pp);
+       }
+
+       /* Flush out and synchronise all the displays: */
+       sendcom(ALL_PLAYERS, REFRESH);
+}
+
+/*
+ * move_normal_shot:
+ *     Move a normal shot along its trajectory.
+ *     Returns false if the bullet no longer needs tracking.
+ */
+static int
+move_normal_shot(bp)
+       BULLET  *bp;
+{
+       int     i, x, y;
+       PLAYER  *pp;
+
+       /*
+        * Walk an unexploded bullet along conf_bulspd times, moving it
+        * one unit along each step. We flag it as exploding if it
+        * meets something.
+        */
+
+       for (i = 0; i < conf_bulspd; i++) {
+
+               /* Stop if the bullet has already exploded: */
+               if (bp->b_expl)
+                       break;
+
+               /* Adjust the bullet's co-ordinates: */
+               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;
+               }
+
+
+               /* Look at what the bullet is colliding with : */
+               switch (Maze[y][x]) {
+                 /* Gun shots have a chance of collision: */
+                 case SHOT:
+                       if (rand_num(100) < conf_pshot_coll) {
+                               zapshot(Bullets, bp);
+                               zapshot(bp->b_next, bp);
+                       }
+                       break;
+                 /* Grenades only have a chance of collision: */
+                 case GRENADE:
+                       if (rand_num(100) < conf_pgren_coll) {
+                               zapshot(Bullets, bp);
+                               zapshot(bp->b_next, bp);
+                       }
+                       break;
+                 /* Reflecting walls richochet the bullet: */
+                 case WALL4:
+                       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;
+                       for (pp = Monitor; pp < End_monitor; pp++)
+                               check(pp, y, x);
+                       break;
+                 case WALL5:
+                       switch (bp->b_face) {
+                         case LEFTS:
+                               bp->b_face = ABOVE;
<