# @(#)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 \
grdc \
hack \
hangman \
+ hunt \
larn \
mille \
morse \
--- /dev/null
+# $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>
--- /dev/null
+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 $
--- /dev/null
+
+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 $
--- /dev/null
+# $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>
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
--- /dev/null
+.\" $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.
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+# $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>
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 *);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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,
+ };
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+.\" $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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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];
--- /dev/null
+/*
+ * 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:
+ &nbs