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