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