Home | History | Annotate | Line # | Download | only in huntd
driver.c revision 1.21
      1 /*	$NetBSD: driver.c,v 1.21 2011/08/31 16:24:56 plunky Exp $	*/
      2 /*
      3  * Copyright (c) 1983-2003, Regents of the University of California.
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions are
      8  * met:
      9  *
     10  * + Redistributions of source code must retain the above copyright
     11  *   notice, this list of conditions and the following disclaimer.
     12  * + Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  * + Neither the name of the University of California, San Francisco nor
     16  *   the names of its contributors may be used to endorse or promote
     17  *   products derived from this software without specific prior written
     18  *   permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
     23  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 #ifndef lint
     35 __RCSID("$NetBSD: driver.c,v 1.21 2011/08/31 16:24:56 plunky Exp $");
     36 #endif /* not lint */
     37 
     38 #include <sys/ioctl.h>
     39 #include <sys/stat.h>
     40 #include <sys/time.h>
     41 #include <err.h>
     42 #include <errno.h>
     43 #include <signal.h>
     44 #include <stdlib.h>
     45 #include <unistd.h>
     46 #include"hunt.h"
     47 
     48 #ifndef pdp11
     49 #define RN	(((Seed = Seed * 11109 + 13849) >> 16) & 0xffff)
     50 #else
     51 #define RN	((Seed = Seed * 11109 + 13849) & 0x7fff)
     52 #endif
     53 
     54 static int Seed = 0;
     55 
     56 
     57 static SOCKET Daemon;
     58 static char *First_arg;			/* pointer to argv[0] */
     59 static char *Last_arg;			/* pointer to end of argv/environ */
     60 
     61 #ifdef INTERNET
     62 static int Test_socket;			/* test socket to answer datagrams */
     63 static FLAG inetd_spawned;		/* invoked via inetd */
     64 static FLAG standard_port = TRUE;	/* true if listening on standard port */
     65 static u_short	sock_port;		/* port # of tcp listen socket */
     66 static u_short	stat_port;		/* port # of statistics tcp socket */
     67 #define DAEMON_SIZE	(sizeof Daemon)
     68 #else
     69 #define DAEMON_SIZE	(sizeof Daemon - 1)
     70 #endif
     71 
     72 static void clear_scores(void);
     73 static int havechar(PLAYER *, int);
     74 static void init(void);
     75 int main(int, char *[], char *[]);
     76 static void makeboots(void);
     77 static void send_stats(void);
     78 static void zap(PLAYER *, FLAG, int);
     79 
     80 
     81 /*
     82  * main:
     83  *	The main program.
     84  */
     85 int
     86 main(int ac, char **av, char **ep)
     87 {
     88 	PLAYER *pp;
     89 #ifdef INTERNET
     90 	u_short msg;
     91 	short port_num, reply;
     92 	socklen_t namelen;
     93 	SOCKET test;
     94 #endif
     95 	static FLAG first = TRUE;
     96 	static FLAG server = FALSE;
     97 	int c, i;
     98 	const int linger = 90 * 1000;
     99 
    100 	First_arg = av[0];
    101 	if (ep == NULL || *ep == NULL)
    102 		ep = av + ac;
    103 	while (*ep)
    104 		ep++;
    105 	Last_arg = ep[-1] + strlen(ep[-1]);
    106 
    107 	while ((c = getopt(ac, av, "sp:")) != -1) {
    108 		switch (c) {
    109 		  case 's':
    110 			server = TRUE;
    111 			break;
    112 #ifdef INTERNET
    113 		  case 'p':
    114 			standard_port = FALSE;
    115 			Test_port = atoi(optarg);
    116 			break;
    117 #endif
    118 		  default:
    119 erred:
    120 			fprintf(stderr, "Usage: %s [-s] [-p port]\n", av[0]);
    121 			exit(1);
    122 		}
    123 	}
    124 	if (optind < ac)
    125 		goto erred;
    126 
    127 	init();
    128 
    129 
    130 again:
    131 	do {
    132 		errno = 0;
    133 		while (poll(fdset, 3+MAXPL+MAXMON, INFTIM) < 0)
    134 		{
    135 			if (errno != EINTR)
    136 #ifdef LOG
    137 				syslog(LOG_WARNING, "poll: %m");
    138 #else
    139 				warn("poll");
    140 #endif
    141 			errno = 0;
    142 		}
    143 #ifdef INTERNET
    144 		if (fdset[2].revents & POLLIN) {
    145 			namelen = DAEMON_SIZE;
    146 			port_num = htons(sock_port);
    147 			(void) recvfrom(Test_socket, &msg, sizeof msg,
    148 				0, (struct sockaddr *) &test, &namelen);
    149 			switch (ntohs(msg)) {
    150 			  case C_MESSAGE:
    151 				if (Nplayer <= 0)
    152 					break;
    153 				reply = htons((u_short) Nplayer);
    154 				(void) sendto(Test_socket, &reply,
    155 					sizeof reply, 0,
    156 					(struct sockaddr *) &test, DAEMON_SIZE);
    157 				break;
    158 			  case C_SCORES:
    159 				reply = htons(stat_port);
    160 				(void) sendto(Test_socket, &reply,
    161 					sizeof reply, 0,
    162 					(struct sockaddr *) &test, DAEMON_SIZE);
    163 				break;
    164 			  case C_PLAYER:
    165 			  case C_MONITOR:
    166 				if (msg == C_MONITOR && Nplayer <= 0)
    167 					break;
    168 				reply = htons(sock_port);
    169 				(void) sendto(Test_socket, &reply,
    170 					sizeof reply, 0,
    171 					(struct sockaddr *) &test, DAEMON_SIZE);
    172 				break;
    173 			}
    174 		}
    175 #endif
    176 		{
    177 			for (pp = Player, i = 0; pp < End_player; pp++, i++)
    178 				if (havechar(pp, i + 3)) {
    179 					execute(pp);
    180 					pp->p_nexec++;
    181 				}
    182 #ifdef MONITOR
    183 			for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++)
    184 				if (havechar(pp, i + MAXPL + 3)) {
    185 					mon_execute(pp);
    186 					pp->p_nexec++;
    187 				}
    188 #endif
    189 			moveshots();
    190 			for (pp = Player, i = 0; pp < End_player; )
    191 				if (pp->p_death[0] != '\0')
    192 					zap(pp, TRUE, i + 3);
    193 				else
    194 					pp++, i++;
    195 #ifdef MONITOR
    196 			for (pp = Monitor, i = 0; pp < End_monitor; )
    197 				if (pp->p_death[0] != '\0')
    198 					zap(pp, FALSE, i + MAXPL + 3);
    199 				else
    200 					pp++, i++;
    201 #endif
    202 		}
    203 		if (fdset[0].revents & POLLIN)
    204 			if (answer()) {
    205 #ifdef INTERNET
    206 				if (first && standard_port)
    207 					faketalk();
    208 #endif
    209 				first = FALSE;
    210 			}
    211 		if (fdset[1].revents & POLLIN)
    212 			send_stats();
    213 		for (pp = Player, i = 0; pp < End_player; pp++, i++) {
    214 			if (fdset[i + 3].revents & POLLIN)
    215 				sendcom(pp, READY, pp->p_nexec);
    216 			pp->p_nexec = 0;
    217 			(void) fflush(pp->p_output);
    218 		}
    219 #ifdef MONITOR
    220 		for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++) {
    221 			if (fdset[i + MAXPL + 3].revents & POLLIN)
    222 				sendcom(pp, READY, pp->p_nexec);
    223 			pp->p_nexec = 0;
    224 			(void) fflush(pp->p_output);
    225 		}
    226 #endif
    227 	} while (Nplayer > 0);
    228 
    229 	if (poll(fdset, 3+MAXPL+MAXMON, linger) > 0) {
    230 		goto again;
    231 	}
    232 	if (server) {
    233 		clear_scores();
    234 		makemaze();
    235 		clearwalls();
    236 #ifdef BOOTS
    237 		makeboots();
    238 #endif
    239 		first = TRUE;
    240 		goto again;
    241 	}
    242 
    243 #ifdef MONITOR
    244 	for (pp = Monitor, i = 0; pp < End_monitor; i++)
    245 		zap(pp, FALSE, i + MAXPL + 3);
    246 #endif
    247 	cleanup(0);
    248 	/* NOTREACHED */
    249 	return(0);
    250 }
    251 
    252 /*
    253  * init:
    254  *	Initialize the global parameters.
    255  */
    256 static void
    257 init(void)
    258 {
    259 	int i;
    260 #ifdef INTERNET
    261 	SOCKET test_port;
    262 	int msg;
    263 	socklen_t len;
    264 #endif
    265 
    266 #ifndef DEBUG
    267 #ifdef TIOCNOTTY
    268 	(void) ioctl(fileno(stdout), TIOCNOTTY, NULL);
    269 #endif
    270 	(void) setpgrp(getpid(), getpid());
    271 	(void) signal(SIGHUP, SIG_IGN);
    272 	(void) signal(SIGINT, SIG_IGN);
    273 	(void) signal(SIGQUIT, SIG_IGN);
    274 	(void) signal(SIGTERM, cleanup);
    275 #endif
    276 
    277 	(void) chdir("/var/tmp");	/* just in case it core dumps */
    278 	(void) umask(0);		/* No privacy at all! */
    279 	(void) signal(SIGPIPE, SIG_IGN);
    280 
    281 #ifdef LOG
    282 	openlog("huntd", LOG_PID, LOG_DAEMON);
    283 #endif
    284 
    285 	/*
    286 	 * Initialize statistics socket
    287 	 */
    288 #ifdef INTERNET
    289 	Daemon.sin_family = SOCK_FAMILY;
    290 	Daemon.sin_addr.s_addr = INADDR_ANY;
    291 	Daemon.sin_port = 0;
    292 #else
    293 	Daemon.sun_family = SOCK_FAMILY;
    294 	(void) strcpy(Daemon.sun_path, Stat_name);
    295 #endif
    296 
    297 	Status = socket(SOCK_FAMILY, SOCK_STREAM, 0);
    298 	if (bind(Status, (struct sockaddr *) &Daemon, DAEMON_SIZE) < 0) {
    299 		if (errno == EADDRINUSE)
    300 			exit(0);
    301 		else {
    302 #ifdef LOG
    303 			syslog(LOG_ERR, "bind: %m");
    304 #else
    305 			warn("bind");
    306 #endif
    307 			cleanup(1);
    308 		}
    309 	}
    310 	(void) listen(Status, 5);
    311 
    312 #ifdef INTERNET
    313 	len = sizeof (SOCKET);
    314 	if (getsockname(Status, (struct sockaddr *) &Daemon, &len) < 0)  {
    315 #ifdef LOG
    316 		syslog(LOG_ERR, "getsockname: %m");
    317 #else
    318 		warn("getsockname");
    319 #endif
    320 		exit(1);
    321 	}
    322 	stat_port = ntohs(Daemon.sin_port);
    323 #endif
    324 
    325 	/*
    326 	 * Initialize main socket
    327 	 */
    328 #ifdef INTERNET
    329 	Daemon.sin_family = SOCK_FAMILY;
    330 	Daemon.sin_addr.s_addr = INADDR_ANY;
    331 	Daemon.sin_port = 0;
    332 #else
    333 	Daemon.sun_family = SOCK_FAMILY;
    334 	(void) strcpy(Daemon.sun_path, Sock_name);
    335 #endif
    336 
    337 	Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
    338 #if defined(INTERNET)
    339 	msg = 1;
    340 	if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK, &msg, sizeof msg)<0)
    341 #ifdef LOG
    342 		syslog(LOG_WARNING, "setsockopt loopback %m");
    343 #else
    344 		warn("setsockopt loopback");
    345 #endif
    346 #endif
    347 	if (bind(Socket, (struct sockaddr *) &Daemon, DAEMON_SIZE) < 0) {
    348 		if (errno == EADDRINUSE)
    349 			exit(0);
    350 		else {
    351 #ifdef LOG
    352 			syslog(LOG_ERR, "bind: %m");
    353 #else
    354 			warn("bind");
    355 #endif
    356 			cleanup(1);
    357 		}
    358 	}
    359 	(void) listen(Socket, 5);
    360 
    361 #ifdef INTERNET
    362 	len = sizeof (SOCKET);
    363 	if (getsockname(Socket, (struct sockaddr *) &Daemon, &len) < 0)  {
    364 #ifdef LOG
    365 		syslog(LOG_ERR, "getsockname: %m");
    366 #else
    367 		warn("getsockname");
    368 #endif
    369 		exit(1);
    370 	}
    371 	sock_port = ntohs(Daemon.sin_port);
    372 #endif
    373 
    374 	/*
    375 	 * Initialize minimal poll mask
    376 	 */
    377 	fdset[0].fd = Socket;
    378 	fdset[0].events = POLLIN;
    379 	fdset[1].fd = Status;
    380 	fdset[1].events = POLLIN;
    381 
    382 #ifdef INTERNET
    383 	len = sizeof (SOCKET);
    384 	if (getsockname(0, (struct sockaddr *) &test_port, &len) >= 0
    385 	&& test_port.sin_family == AF_INET) {
    386 		inetd_spawned = TRUE;
    387 		Test_socket = 0;
    388 		if (test_port.sin_port != htons((u_short) Test_port)) {
    389 			standard_port = FALSE;
    390 			Test_port = ntohs(test_port.sin_port);
    391 		}
    392 	} else {
    393 		test_port = Daemon;
    394 		test_port.sin_port = htons((u_short) Test_port);
    395 
    396 		Test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
    397 		if (bind(Test_socket, (struct sockaddr *) &test_port,
    398 		    DAEMON_SIZE) < 0) {
    399 #ifdef LOG
    400 			syslog(LOG_ERR, "bind: %m");
    401 #else
    402 			warn("bind");
    403 #endif
    404 			exit(1);
    405 		}
    406 		(void) listen(Test_socket, 5);
    407 	}
    408 
    409 	fdset[2].fd = Test_socket;
    410 	fdset[2].events = POLLIN;
    411 #else
    412 	fdset[2].fd = -1;
    413 #endif
    414 
    415 	Seed = getpid() + time(NULL);
    416 	makemaze();
    417 #ifdef BOOTS
    418 	makeboots();
    419 #endif
    420 
    421 	for (i = 0; i < NASCII; i++)
    422 		See_over[i] = TRUE;
    423 	See_over[DOOR] = FALSE;
    424 	See_over[WALL1] = FALSE;
    425 	See_over[WALL2] = FALSE;
    426 	See_over[WALL3] = FALSE;
    427 #ifdef REFLECT
    428 	See_over[WALL4] = FALSE;
    429 	See_over[WALL5] = FALSE;
    430 #endif
    431 
    432 }
    433 
    434 #ifdef BOOTS
    435 /*
    436  * makeboots:
    437  *	Put the boots in the maze
    438  */
    439 static void
    440 makeboots(void)
    441 {
    442 	int x, y;
    443 	PLAYER *pp;
    444 
    445 	do {
    446 		x = rand_num(WIDTH - 1) + 1;
    447 		y = rand_num(HEIGHT - 1) + 1;
    448 	} while (Maze[y][x] != SPACE);
    449 	Maze[y][x] = BOOT_PAIR;
    450 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
    451 		pp->p_flying = -1;
    452 }
    453 #endif
    454 
    455 
    456 /*
    457  * checkdam:
    458  *	Check the damage to the given player, and see if s/he is killed
    459  */
    460 void
    461 checkdam(PLAYER *ouch, PLAYER *gotcha, IDENT *credit, int amt,
    462 	 char this_shot_type)
    463 {
    464 	const char *cp;
    465 
    466 	if (ouch->p_death[0] != '\0')
    467 		return;
    468 #ifdef BOOTS
    469 	if (this_shot_type == SLIME)
    470 		switch (ouch->p_nboots) {
    471 		  default:
    472 			break;
    473 		  case 1:
    474 			amt = (amt + 1) / 2;
    475 			break;
    476 		  case 2:
    477 			if (gotcha != NULL)
    478 				message(gotcha, "He has boots on!");
    479 			return;
    480 		}
    481 #endif
    482 	ouch->p_damage += amt;
    483 	if (ouch->p_damage <= ouch->p_damcap) {
    484 		(void) snprintf(Buf, sizeof(Buf), "%2d", ouch->p_damage);
    485 		cgoto(ouch, STAT_DAM_ROW, STAT_VALUE_COL);
    486 		outstr(ouch, Buf, 2);
    487 		return;
    488 	}
    489 
    490 	/* Someone DIED */
    491 	switch (this_shot_type) {
    492 	  default:
    493 		cp = "Killed";
    494 		break;
    495 #ifdef FLY
    496 	  case FALL:
    497 		cp = "Killed on impact";
    498 		break;
    499 #endif
    500 	  case KNIFE:
    501 		cp = "Stabbed to death";
    502 		ouch->p_ammo = 0;		/* No exploding */
    503 		break;
    504 	  case SHOT:
    505 		cp = "Shot to death";
    506 		break;
    507 	  case GRENADE:
    508 	  case SATCHEL:
    509 	  case BOMB:
    510 		cp = "Bombed";
    511 		break;
    512 	  case MINE:
    513 	  case GMINE:
    514 		cp = "Blown apart";
    515 		break;
    516 #ifdef	OOZE
    517 	  case SLIME:
    518 		cp = "Slimed";
    519 		if (credit != NULL)
    520 			credit->i_slime++;
    521 		break;
    522 #endif
    523 #ifdef	VOLCANO
    524 	  case LAVA:
    525 		cp = "Baked";
    526 		break;
    527 #endif
    528 #ifdef DRONE
    529 	  case DSHOT:
    530 		cp = "Eliminated";
    531 		break;
    532 #endif
    533 	}
    534 	if (credit == NULL) {
    535 		(void) snprintf(ouch->p_death, sizeof(ouch->p_death),
    536 			"| %s by %s |", cp,
    537 			(this_shot_type == MINE || this_shot_type == GMINE) ?
    538 			"a mine" : "act of God");
    539 		return;
    540 	}
    541 
    542 	(void) snprintf(ouch->p_death, sizeof(ouch->p_death),
    543 		"| %s by %s |", cp, credit->i_name);
    544 
    545 	if (ouch == gotcha) {		/* No use killing yourself */
    546 		credit->i_kills--;
    547 		credit->i_bkills++;
    548 	}
    549 	else if (ouch->p_ident->i_team == ' '
    550 	|| ouch->p_ident->i_team != credit->i_team) {
    551 		credit->i_kills++;
    552 		credit->i_gkills++;
    553 	}
    554 	else {
    555 		credit->i_kills--;
    556 		credit->i_bkills++;
    557 	}
    558 	credit->i_score = credit->i_kills / (double) credit->i_entries;
    559 	ouch->p_ident->i_deaths++;
    560 	if (ouch->p_nchar == 0)
    561 		ouch->p_ident->i_stillb++;
    562 	if (gotcha == NULL)
    563 		return;
    564 	gotcha->p_damcap += STABDAM;
    565 	gotcha->p_damage -= STABDAM;
    566 	if (gotcha->p_damage < 0)
    567 		gotcha->p_damage = 0;
    568 	(void) snprintf(Buf, sizeof(Buf), "%2d/%2d", gotcha->p_damage,
    569 			gotcha->p_damcap);
    570 	cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL);
    571 	outstr(gotcha, Buf, 5);
    572 	(void) snprintf(Buf, sizeof(Buf), "%3d",
    573 			(gotcha->p_damcap - MAXDAM) / 2);
    574 	cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL);
    575 	outstr(gotcha, Buf, 3);
    576 	(void) snprintf(Buf, sizeof(Buf), "%5.2f", gotcha->p_ident->i_score);
    577 	for (ouch = Player; ouch < End_player; ouch++) {
    578 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
    579 			STAT_NAME_COL);
    580 		outstr(ouch, Buf, 5);
    581 	}
    582 #ifdef MONITOR
    583 	for (ouch = Monitor; ouch < End_monitor; ouch++) {
    584 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
    585 			STAT_NAME_COL);
    586 		outstr(ouch, Buf, 5);
    587 	}
    588 #endif
    589 }
    590 
    591 /*
    592  * zap:
    593  *	Kill off a player and take him out of the game.
    594  */
    595 static void
    596 zap(PLAYER *pp, FLAG was_player, int i)
    597 {
    598 	int n, len;
    599 	BULLET *bp;
    600 	PLAYER *np;
    601 	int x, y;
    602 	int savefd;
    603 
    604 	if (was_player) {
    605 		if (pp->p_undershot)
    606 			fixshots(pp->p_y, pp->p_x, pp->p_over);
    607 		drawplayer(pp, FALSE);
    608 		Nplayer--;
    609 	}
    610 
    611 	len = strlen(pp->p_death);	/* Display the cause of death */
    612 	x = (WIDTH - len) / 2;
    613 	cgoto(pp, HEIGHT / 2, x);
    614 	outstr(pp, pp->p_death, len);
    615 	for (n = 1; n < len; n++)
    616 		pp->p_death[n] = '-';
    617 	pp->p_death[0] = '+';
    618 	pp->p_death[len - 1] = '+';
    619 	cgoto(pp, HEIGHT / 2 - 1, x);
    620 	outstr(pp, pp->p_death, len);
    621 	cgoto(pp, HEIGHT / 2 + 1, x);
    622 	outstr(pp, pp->p_death, len);
    623 	cgoto(pp, HEIGHT, 0);
    624 
    625 	savefd = pp->p_fd;
    626 
    627 #ifdef MONITOR
    628 	if (was_player) {
    629 #endif
    630 		for (bp = Bullets; bp != NULL; bp = bp->b_next) {
    631 			if (bp->b_owner == pp)
    632 				bp->b_owner = NULL;
    633 			if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
    634 				bp->b_over = SPACE;
    635 		}
    636 
    637 		n = rand_num(pp->p_ammo);
    638 		x = rand_num(pp->p_ammo);
    639 		if (x > n)
    640 			n = x;
    641 		if (pp->p_ammo == 0)
    642 			x = 0;
    643 		else if (n == pp->p_ammo - 1) {
    644 			x = pp->p_ammo;
    645 			len = SLIME;
    646 		}
    647 		else {
    648 			for (x = MAXBOMB - 1; x > 0; x--)
    649 				if (n >= shot_req[x])
    650 					break;
    651 			for (y = MAXSLIME - 1; y > 0; y--)
    652 				if (n >= slime_req[y])
    653 					break;
    654 			if (y >= 0 && slime_req[y] > shot_req[x]) {
    655 				x = slime_req[y];
    656 				len = SLIME;
    657 			}
    658 			else if (x != 0) {
    659 				len = shot_type[x];
    660 				x = shot_req[x];
    661 			}
    662 		}
    663 		if (x > 0) {
    664 			(void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x,
    665 				NULL, TRUE, SPACE);
    666 			(void) snprintf(Buf, sizeof(Buf), "%s detonated.",
    667 				pp->p_ident->i_name);
    668 			for (np = Player; np < End_player; np++)
    669 				message(np, Buf);
    670 #ifdef MONITOR
    671 			for (np = Monitor; np < End_monitor; np++)
    672 				message(np, Buf);
    673 #endif
    674 #ifdef BOOTS
    675 			while (pp->p_nboots-- > 0) {
    676 				for (np = Boot; np < &Boot[NBOOTS]; np++)
    677 					if (np->p_flying < 0)
    678 						break;
    679 				if (np >= &Boot[NBOOTS])
    680 					err(1, "Too many boots");
    681 				np->p_undershot = FALSE;
    682 				np->p_x = pp->p_x;
    683 				np->p_y = pp->p_y;
    684 				np->p_flying = rand_num(20);
    685 				np->p_flyx = 2 * rand_num(6) - 5;
    686 				np->p_flyy = 2 * rand_num(6) - 5;
    687 				np->p_over = SPACE;
    688 				np->p_face = BOOT;
    689 				showexpl(np->p_y, np->p_x, BOOT);
    690 			}
    691 #endif
    692 		}
    693 #ifdef BOOTS
    694 		else if (pp->p_nboots > 0) {
    695 			if (pp->p_nboots == 2)
    696 				Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
    697 			else
    698 				Maze[pp->p_y][pp->p_x] = BOOT;
    699 			if (pp->p_undershot)
    700 				fixshots(pp->p_y, pp->p_x,
    701 					Maze[pp->p_y][pp->p_x]);
    702 		}
    703 #endif
    704 
    705 #ifdef VOLCANO
    706 		volcano += pp->p_ammo - x;
    707 		if (rand_num(100) < volcano / 50) {
    708 			do {
    709 				x = rand_num(WIDTH / 2) + WIDTH / 4;
    710 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
    711 			} while (Maze[y][x] != SPACE);
    712 			(void) add_shot(LAVA, y, x, LEFTS, volcano,
    713 				NULL, TRUE, SPACE);
    714 			for (np = Player; np < End_player; np++)
    715 				message(np, "Volcano eruption.");
    716 			volcano = 0;
    717 		}
    718 #endif
    719 
    720 #ifdef DRONE
    721 		if (rand_num(100) < 2) {
    722 			do {
    723 				x = rand_num(WIDTH / 2) + WIDTH / 4;
    724 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
    725 			} while (Maze[y][x] != SPACE);
    726 			add_shot(DSHOT, y, x, rand_dir(),
    727 				shot_req[MINDSHOT +
    728 				rand_num(MAXBOMB - MINDSHOT)],
    729 				NULL, FALSE, SPACE);
    730 		}
    731 #endif
    732 
    733 		sendcom(pp, ENDWIN);
    734 		(void) putc(' ', pp->p_output);
    735 		(void) fclose(pp->p_output);
    736 
    737 		End_player--;
    738 		if (pp != End_player) {
    739 			memcpy(pp, End_player, sizeof (PLAYER));
    740 			fdset[i] = fdset[End_player - Player + 3];
    741 			fdset[End_player - Player + 3].fd = -1;
    742 			(void) snprintf(Buf, sizeof(Buf), "%5.2f%c%-10.10s %c",
    743 				pp->p_ident->i_score, stat_char(pp),
    744 				pp->p_ident->i_name, pp->p_ident->i_team);
    745 			n = STAT_PLAY_ROW + 1 + (pp - Player);
    746 			for (np = Player; np < End_player; np++) {
    747 				cgoto(np, n, STAT_NAME_COL);
    748 				outstr(np, Buf, STAT_NAME_LEN);
    749 			}
    750 #ifdef MONITOR
    751 			for (np = Monitor; np < End_monitor; np++) {
    752 				cgoto(np, n, STAT_NAME_COL);
    753 				outstr(np, Buf, STAT_NAME_LEN);
    754 			}
    755 #endif
    756 		} else
    757 			fdset[i].fd = -1;
    758 
    759 		/* Erase the last player */
    760 		n = STAT_PLAY_ROW + 1 + Nplayer;
    761 		for (np = Player; np < End_player; np++) {
    762 			cgoto(np, n, STAT_NAME_COL);
    763 			ce(np);
    764 		}
    765 #ifdef MONITOR
    766 		for (np = Monitor; np < End_monitor; np++) {
    767 			cgoto(np, n, STAT_NAME_COL);
    768 			ce(np);
    769 		}
    770 	}
    771 	else {
    772 		sendcom(pp, ENDWIN);
    773 		(void) putc(LAST_PLAYER, pp->p_output);
    774 		(void) fclose(pp->p_output);
    775 
    776 		End_monitor--;
    777 		if (pp != End_monitor) {
    778 			memcpy(pp, End_monitor, sizeof (PLAYER));
    779 			fdset[i] = fdset[End_monitor - Monitor + MAXPL + 3];
    780 			fdset[End_monitor - Monitor + MAXPL + 3].fd = -1;
    781 			(void) snprintf(Buf, sizeof(Buf), "%5.5s %-10.10s %c",
    782 				" ",
    783 				pp->p_ident->i_name, pp->p_ident->i_team);
    784 			n = STAT_MON_ROW + 1 + (pp - Player);
    785 			for (np = Player; np < End_player; np++) {
    786 				cgoto(np, n, STAT_NAME_COL);
    787 				outstr(np, Buf, STAT_NAME_LEN);
    788 			}
    789 			for (np = Monitor; np < End_monitor; np++) {
    790 				cgoto(np, n, STAT_NAME_COL);
    791 				outstr(np, Buf, STAT_NAME_LEN);
    792 			}
    793 		} else
    794 			fdset[i].fd = -1;
    795 
    796 		/* Erase the last monitor */
    797 		n = STAT_MON_ROW + 1 + (End_monitor - Monitor);
    798 		for (np = Player; np < End_player; np++) {
    799 			cgoto(np, n, STAT_NAME_COL);
    800 			ce(np);
    801 		}
    802 		for (np = Monitor; np < End_monitor; np++) {
    803 			cgoto(np, n, STAT_NAME_COL);
    804 			ce(np);
    805 		}
    806 	}
    807 #endif
    808 }
    809 
    810 /*
    811  * rand_num:
    812  *	Return a random number in a given range.
    813  */
    814 int
    815 rand_num(int range)
    816 {
    817 	return (range == 0 ? 0 : RN % range);
    818 }
    819 
    820 /*
    821  * havechar:
    822  *	Check to see if we have any characters in the input queue; if
    823  *	we do, read them, stash them away, and return TRUE; else return
    824  *	FALSE.
    825  */
    826 static int
    827 havechar(PLAYER *pp, int i)
    828 {
    829 
    830 	if (pp->p_ncount < pp->p_nchar)
    831 		return TRUE;
    832 	if (!(fdset[i].revents & POLLIN))
    833 		return FALSE;
    834 check_again:
    835 	errno = 0;
    836 	if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0)
    837 	{
    838 		if (errno == EINTR)
    839 			goto check_again;
    840 		pp->p_cbuf[0] = 'q';
    841 	}
    842 	pp->p_ncount = 0;
    843 	return TRUE;
    844 }
    845 
    846 /*
    847  * cleanup:
    848  *	Exit with the given value, cleaning up any droppings lying around
    849  */
    850 void
    851 cleanup(int eval)
    852 {
    853 	PLAYER *pp;
    854 
    855 	for (pp = Player; pp < End_player; pp++) {
    856 		cgoto(pp, HEIGHT, 0);
    857 		sendcom(pp, ENDWIN);
    858 		(void) putc(LAST_PLAYER, pp->p_output);
    859 		(void) fclose(pp->p_output);
    860 	}
    861 #ifdef MONITOR
    862 	for (pp = Monitor; pp < End_monitor; pp++) {
    863 		cgoto(pp, HEIGHT, 0);
    864 		sendcom(pp, ENDWIN);
    865 		(void) putc(LAST_PLAYER, pp->p_output);
    866 		(void) fclose(pp->p_output);
    867 	}
    868 #endif
    869 	(void) close(Socket);
    870 #ifdef AF_UNIX_HACK
    871 	(void) unlink(Sock_name);
    872 #endif
    873 
    874 	exit(eval);
    875 }
    876 
    877 /*
    878  * send_stats:
    879  *	Print stats to requestor
    880  */
    881 static void
    882 send_stats(void)
    883 {
    884 	IDENT *ip;
    885 	FILE *fp;
    886 	int s;
    887 	SOCKET sockstruct;
    888 	socklen_t socklen;
    889 
    890 	/*
    891 	 * Get the output stream ready
    892 	 */
    893 #ifdef INTERNET
    894 	socklen = sizeof sockstruct;
    895 #else
    896 	socklen = sizeof sockstruct - 1;
    897 #endif
    898 	s = accept(Status, (struct sockaddr *) &sockstruct, &socklen);
    899 	if (s < 0) {
    900 		if (errno == EINTR)
    901 			return;
    902 #ifdef LOG
    903 		syslog(LOG_WARNING, "accept: %m");
    904 #else
    905 		warn("accept");
    906 #endif
    907 		return;
    908 	}
    909 	fp = fdopen(s, "w");
    910 	if (fp == NULL) {
    911 #ifdef LOG
    912 		syslog(LOG_WARNING, "fdopen: %m");
    913 #else
    914 		warn("fdopen");
    915 #endif
    916 		(void) close(s);
    917 		return;
    918 	}
    919 
    920 	/*
    921 	 * Send output to requestor
    922 	 */
    923 	fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
    924 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
    925 		fprintf(fp, "%s\t", ip->i_name);
    926 		if (strlen(ip->i_name) < 8)
    927 			putc('\t', fp);
    928 		fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
    929 			ip->i_score, ip->i_ducked, ip->i_absorbed,
    930 			ip->i_faced, ip->i_shot, ip->i_robbed,
    931 			ip->i_missed, ip->i_slime);
    932 	}
    933 	fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp);
    934 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
    935 		if (ip->i_team == ' ') {
    936 			fprintf(fp, "%s\t", ip->i_name);
    937 			if (strlen(ip->i_name) < 8)
    938 				putc('\t', fp);
    939 		}
    940 		else {
    941 			fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team);
    942 			if (strlen(ip->i_name) + 3 < 8)
    943 				putc('\t', fp);
    944 		}
    945 		fprintf(fp, "%d\t%d\t%d\t%d\t%d\n",
    946 			ip->i_gkills, ip->i_bkills, ip->i_deaths,
    947 			ip->i_stillb, ip->i_saved);
    948 	}
    949 
    950 	(void) fclose(fp);
    951 }
    952 
    953 /*
    954  * clear_scores:
    955  *	Clear out the scores so the next session start clean
    956  */
    957 static void
    958 clear_scores(void)
    959 {
    960 	IDENT *ip, *nextip;
    961 
    962 	for (ip = Scores; ip != NULL; ip = nextip) {
    963 		nextip = ip->i_next;
    964 		(void) free(ip);
    965 	}
    966 	Scores = NULL;
    967 }
    968