Home | History | Annotate | Line # | Download | only in huntd
driver.c revision 1.9
      1 /*	$NetBSD: driver.c,v 1.9 2003/06/11 12:00:22 wiz 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.9 2003/06/11 12:00:22 wiz 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 __P((void));
     72 static	int	havechar __P((PLAYER *, int));
     73 static	void	init __P((void));
     74 	int	main __P((int, char *[], char *[]));
     75 static	void	makeboots __P((void));
     76 static	void	send_stats __P((void));
     77 static	void	zap __P((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 	int	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, "select: %m");
    139 # else
    140 				warn("select");
    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, (char *) &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, (char *) &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, (char *) &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, (char *) &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 	int	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 select 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, shot_type)
    468 	PLAYER	*ouch, *gotcha;
    469 	IDENT	*credit;
    470 	int	amt;
    471 	char	shot_type;
    472 {
    473 	char	*cp;
    474 
    475 	if (ouch->p_death[0] != '\0')
    476 		return;
    477 # ifdef BOOTS
    478 	if (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) sprintf(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 (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) sprintf(ouch->p_death, "| %s by %s |", cp,
    545 			(shot_type == MINE || shot_type == GMINE) ?
    546 			"a mine" : "act of God");
    547 		return;
    548 	}
    549 
    550 	(void) sprintf(ouch->p_death, "| %s by %s |", cp, credit->i_name);
    551 
    552 	if (ouch == gotcha) {		/* No use killing yourself */
    553 		credit->i_kills--;
    554 		credit->i_bkills++;
    555 	}
    556 	else if (ouch->p_ident->i_team == ' '
    557 	|| ouch->p_ident->i_team != credit->i_team) {
    558 		credit->i_kills++;
    559 		credit->i_gkills++;
    560 	}
    561 	else {
    562 		credit->i_kills--;
    563 		credit->i_bkills++;
    564 	}
    565 	credit->i_score = credit->i_kills / (double) credit->i_entries;
    566 	ouch->p_ident->i_deaths++;
    567 	if (ouch->p_nchar == 0)
    568 		ouch->p_ident->i_stillb++;
    569 	if (gotcha == NULL)
    570 		return;
    571 	gotcha->p_damcap += STABDAM;
    572 	gotcha->p_damage -= STABDAM;
    573 	if (gotcha->p_damage < 0)
    574 		gotcha->p_damage = 0;
    575 	(void) sprintf(Buf, "%2d/%2d", gotcha->p_damage, gotcha->p_damcap);
    576 	cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL);
    577 	outstr(gotcha, Buf, 5);
    578 	(void) sprintf(Buf, "%3d", (gotcha->p_damcap - MAXDAM) / 2);
    579 	cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL);
    580 	outstr(gotcha, Buf, 3);
    581 	(void) sprintf(Buf, "%5.2f", gotcha->p_ident->i_score);
    582 	for (ouch = Player; ouch < End_player; ouch++) {
    583 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
    584 			STAT_NAME_COL);
    585 		outstr(ouch, Buf, 5);
    586 	}
    587 # ifdef MONITOR
    588 	for (ouch = Monitor; ouch < End_monitor; ouch++) {
    589 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
    590 			STAT_NAME_COL);
    591 		outstr(ouch, Buf, 5);
    592 	}
    593 # endif
    594 }
    595 
    596 /*
    597  * zap:
    598  *	Kill off a player and take him out of the game.
    599  */
    600 static void
    601 zap(pp, was_player, i)
    602 	PLAYER	*pp;
    603 	FLAG	was_player;
    604 	int	i;
    605 {
    606 	int	n, len;
    607 	BULLET	*bp;
    608 	PLAYER	*np;
    609 	int	x, y;
    610 	int	savefd;
    611 
    612 	if (was_player) {
    613 		if (pp->p_undershot)
    614 			fixshots(pp->p_y, pp->p_x, pp->p_over);
    615 		drawplayer(pp, FALSE);
    616 		Nplayer--;
    617 	}
    618 
    619 	len = strlen(pp->p_death);	/* Display the cause of death */
    620 	x = (WIDTH - len) / 2;
    621 	cgoto(pp, HEIGHT / 2, x);
    622 	outstr(pp, pp->p_death, len);
    623 	for (n = 1; n < len; n++)
    624 		pp->p_death[n] = '-';
    625 	pp->p_death[0] = '+';
    626 	pp->p_death[len - 1] = '+';
    627 	cgoto(pp, HEIGHT / 2 - 1, x);
    628 	outstr(pp, pp->p_death, len);
    629 	cgoto(pp, HEIGHT / 2 + 1, x);
    630 	outstr(pp, pp->p_death, len);
    631 	cgoto(pp, HEIGHT, 0);
    632 
    633 	savefd = pp->p_fd;
    634 
    635 # ifdef MONITOR
    636 	if (was_player) {
    637 # endif
    638 		for (bp = Bullets; bp != NULL; bp = bp->b_next) {
    639 			if (bp->b_owner == pp)
    640 				bp->b_owner = NULL;
    641 			if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
    642 				bp->b_over = SPACE;
    643 		}
    644 
    645 		n = rand_num(pp->p_ammo);
    646 		x = rand_num(pp->p_ammo);
    647 		if (x > n)
    648 			n = x;
    649 		if (pp->p_ammo == 0)
    650 			x = 0;
    651 		else if (n == pp->p_ammo - 1) {
    652 			x = pp->p_ammo;
    653 			len = SLIME;
    654 		}
    655 		else {
    656 			for (x = MAXBOMB - 1; x > 0; x--)
    657 				if (n >= shot_req[x])
    658 					break;
    659 			for (y = MAXSLIME - 1; y > 0; y--)
    660 				if (n >= slime_req[y])
    661 					break;
    662 			if (y >= 0 && slime_req[y] > shot_req[x]) {
    663 				x = slime_req[y];
    664 				len = SLIME;
    665 			}
    666 			else if (x != 0) {
    667 				len = shot_type[x];
    668 				x = shot_req[x];
    669 			}
    670 		}
    671 		if (x > 0) {
    672 			(void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x,
    673 				(PLAYER *) NULL, TRUE, SPACE);
    674 			(void) sprintf(Buf, "%s detonated.",
    675 				pp->p_ident->i_name);
    676 			for (np = Player; np < End_player; np++)
    677 				message(np, Buf);
    678 # ifdef MONITOR
    679 			for (np = Monitor; np < End_monitor; np++)
    680 				message(np, Buf);
    681 # endif
    682 # ifdef BOOTS
    683 			while (pp->p_nboots-- > 0) {
    684 				for (np = Boot; np < &Boot[NBOOTS]; np++)
    685 					if (np->p_flying < 0)
    686 						break;
    687 				if (np >= &Boot[NBOOTS])
    688 					err(1, "Too many boots");
    689 				np->p_undershot = FALSE;
    690 				np->p_x = pp->p_x;
    691 				np->p_y = pp->p_y;
    692 				np->p_flying = rand_num(20);
    693 				np->p_flyx = 2 * rand_num(6) - 5;
    694 				np->p_flyy = 2 * rand_num(6) - 5;
    695 				np->p_over = SPACE;
    696 				np->p_face = BOOT;
    697 				showexpl(np->p_y, np->p_x, BOOT);
    698 			}
    699 # endif
    700 		}
    701 # ifdef BOOTS
    702 		else if (pp->p_nboots > 0) {
    703 			if (pp->p_nboots == 2)
    704 				Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
    705 			else
    706 				Maze[pp->p_y][pp->p_x] = BOOT;
    707 			if (pp->p_undershot)
    708 				fixshots(pp->p_y, pp->p_x,
    709 					Maze[pp->p_y][pp->p_x]);
    710 		}
    711 # endif
    712 
    713 # ifdef VOLCANO
    714 		volcano += pp->p_ammo - x;
    715 		if (rand_num(100) < volcano / 50) {
    716 			do {
    717 				x = rand_num(WIDTH / 2) + WIDTH / 4;
    718 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
    719 			} while (Maze[y][x] != SPACE);
    720 			(void) add_shot(LAVA, y, x, LEFTS, volcano,
    721 				(PLAYER *) NULL, TRUE, SPACE);
    722 			for (np = Player; np < End_player; np++)
    723 				message(np, "Volcano eruption.");
    724 			volcano = 0;
    725 		}
    726 # endif
    727 
    728 # ifdef	DRONE
    729 		if (rand_num(100) < 2) {
    730 			do {
    731 				x = rand_num(WIDTH / 2) + WIDTH / 4;
    732 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
    733 			} while (Maze[y][x] != SPACE);
    734 			add_shot(DSHOT, y, x, rand_dir(),
    735 				shot_req[MINDSHOT +
    736 				rand_num(MAXBOMB - MINDSHOT)],
    737 				(PLAYER *) NULL, FALSE, SPACE);
    738 		}
    739 # endif
    740 
    741 		sendcom(pp, ENDWIN);
    742 		(void) putc(' ', pp->p_output);
    743 		(void) fclose(pp->p_output);
    744 
    745 		End_player--;
    746 		if (pp != End_player) {
    747 			memcpy(pp, End_player, sizeof (PLAYER));
    748 			fdset[i] = fdset[End_player - Player + 3];
    749 			fdset[End_player - Player + 3].fd = -1;
    750 			(void) sprintf(Buf, "%5.2f%c%-10.10s %c",
    751 				pp->p_ident->i_score, stat_char(pp),
    752 				pp->p_ident->i_name, pp->p_ident->i_team);
    753 			n = STAT_PLAY_ROW + 1 + (pp - Player);
    754 			for (np = Player; np < End_player; np++) {
    755 				cgoto(np, n, STAT_NAME_COL);
    756 				outstr(np, Buf, STAT_NAME_LEN);
    757 			}
    758 # ifdef MONITOR
    759 			for (np = Monitor; np < End_monitor; np++) {
    760 				cgoto(np, n, STAT_NAME_COL);
    761 				outstr(np, Buf, STAT_NAME_LEN);
    762 			}
    763 # endif
    764 		} else
    765 			fdset[i].fd = -1;
    766 
    767 		/* Erase the last player */
    768 		n = STAT_PLAY_ROW + 1 + Nplayer;
    769 		for (np = Player; np < End_player; np++) {
    770 			cgoto(np, n, STAT_NAME_COL);
    771 			ce(np);
    772 		}
    773 # ifdef MONITOR
    774 		for (np = Monitor; np < End_monitor; np++) {
    775 			cgoto(np, n, STAT_NAME_COL);
    776 			ce(np);
    777 		}
    778 	}
    779 	else {
    780 		sendcom(pp, ENDWIN);
    781 		(void) putc(LAST_PLAYER, pp->p_output);
    782 		(void) fclose(pp->p_output);
    783 
    784 		End_monitor--;
    785 		if (pp != End_monitor) {
    786 			memcpy(pp, End_monitor, sizeof (PLAYER));
    787 			fdset[i] = fdset[End_monitor - Monitor + MAXPL + 3];
    788 			fdset[End_monitor - Monitor + MAXPL + 3].fd = -1;
    789 			(void) sprintf(Buf, "%5.5s %-10.10s %c", " ",
    790 				pp->p_ident->i_name, pp->p_ident->i_team);
    791 			n = STAT_MON_ROW + 1 + (pp - Player);
    792 			for (np = Player; np < End_player; np++) {
    793 				cgoto(np, n, STAT_NAME_COL);
    794 				outstr(np, Buf, STAT_NAME_LEN);
    795 			}
    796 			for (np = Monitor; np < End_monitor; np++) {
    797 				cgoto(np, n, STAT_NAME_COL);
    798 				outstr(np, Buf, STAT_NAME_LEN);
    799 			}
    800 		} else
    801 			fdset[i].fd = -1;
    802 
    803 		/* Erase the last monitor */
    804 		n = STAT_MON_ROW + 1 + (End_monitor - Monitor);
    805 		for (np = Player; np < End_player; np++) {
    806 			cgoto(np, n, STAT_NAME_COL);
    807 			ce(np);
    808 		}
    809 		for (np = Monitor; np < End_monitor; np++) {
    810 			cgoto(np, n, STAT_NAME_COL);
    811 			ce(np);
    812 		}
    813 	}
    814 # endif
    815 }
    816 
    817 /*
    818  * rand_num:
    819  *	Return a random number in a given range.
    820  */
    821 int
    822 rand_num(range)
    823 	int	range;
    824 {
    825 	return (range == 0 ? 0 : RN % range);
    826 }
    827 
    828 /*
    829  * havechar:
    830  *	Check to see if we have any characters in the input queue; if
    831  *	we do, read them, stash them away, and return TRUE; else return
    832  *	FALSE.
    833  */
    834 static int
    835 havechar(pp, i)
    836 	PLAYER	*pp;
    837 	int	i;
    838 {
    839 
    840 	if (pp->p_ncount < pp->p_nchar)
    841 		return TRUE;
    842 	if (!(fdset[i].revents & POLLIN))
    843 		return FALSE;
    844 check_again:
    845 	errno = 0;
    846 	if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0)
    847 	{
    848 		if (errno == EINTR)
    849 			goto check_again;
    850 		pp->p_cbuf[0] = 'q';
    851 	}
    852 	pp->p_ncount = 0;
    853 	return TRUE;
    854 }
    855 
    856 /*
    857  * cleanup:
    858  *	Exit with the given value, cleaning up any droppings lying around
    859  */
    860 SIGNAL_TYPE
    861 cleanup(eval)
    862 	int	eval;
    863 {
    864 	PLAYER	*pp;
    865 
    866 	for (pp = Player; pp < End_player; pp++) {
    867 		cgoto(pp, HEIGHT, 0);
    868 		sendcom(pp, ENDWIN);
    869 		(void) putc(LAST_PLAYER, pp->p_output);
    870 		(void) fclose(pp->p_output);
    871 	}
    872 # ifdef MONITOR
    873 	for (pp = Monitor; pp < End_monitor; pp++) {
    874 		cgoto(pp, HEIGHT, 0);
    875 		sendcom(pp, ENDWIN);
    876 		(void) putc(LAST_PLAYER, pp->p_output);
    877 		(void) fclose(pp->p_output);
    878 	}
    879 # endif
    880 	(void) close(Socket);
    881 # ifdef AF_UNIX_HACK
    882 	(void) unlink(Sock_name);
    883 # endif
    884 
    885 	exit(eval);
    886 }
    887 
    888 /*
    889  * send_stats:
    890  *	Print stats to requestor
    891  */
    892 static void
    893 send_stats()
    894 {
    895 	IDENT	*ip;
    896 	FILE	*fp;
    897 	int	s;
    898 	SOCKET	sockstruct;
    899 	int	socklen;
    900 
    901 	/*
    902 	 * Get the output stream ready
    903 	 */
    904 # ifdef INTERNET
    905 	socklen = sizeof sockstruct;
    906 # else
    907 	socklen = sizeof sockstruct - 1;
    908 # endif
    909 	s = accept(Status, (struct sockaddr *) &sockstruct, &socklen);
    910 	if (s < 0) {
    911 		if (errno == EINTR)
    912 			return;
    913 # ifdef LOG
    914 		syslog(LOG_WARNING, "accept: %m");
    915 # else
    916 		warn("accept");
    917 # endif
    918 		return;
    919 	}
    920 	fp = fdopen(s, "w");
    921 	if (fp == NULL) {
    922 # ifdef LOG
    923 		syslog(LOG_WARNING, "fdopen: %m");
    924 # else
    925 		warn("fdopen");
    926 # endif
    927 		(void) close(s);
    928 		return;
    929 	}
    930 
    931 	/*
    932 	 * Send output to requestor
    933 	 */
    934 	fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
    935 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
    936 		fprintf(fp, "%s\t", ip->i_name);
    937 		if (strlen(ip->i_name) < 8)
    938 			putc('\t', fp);
    939 		fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
    940 			ip->i_score, ip->i_ducked, ip->i_absorbed,
    941 			ip->i_faced, ip->i_shot, ip->i_robbed,
    942 			ip->i_missed, ip->i_slime);
    943 	}
    944 	fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp);
    945 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
    946 		if (ip->i_team == ' ') {
    947 			fprintf(fp, "%s\t", ip->i_name);
    948 			if (strlen(ip->i_name) < 8)
    949 				putc('\t', fp);
    950 		}
    951 		else {
    952 			fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team);
    953 			if (strlen(ip->i_name) + 3 < 8)
    954 				putc('\t', fp);
    955 		}
    956 		fprintf(fp, "%d\t%d\t%d\t%d\t%d\n",
    957 			ip->i_gkills, ip->i_bkills, ip->i_deaths,
    958 			ip->i_stillb, ip->i_saved);
    959 	}
    960 
    961 	(void) fclose(fp);
    962 }
    963 
    964 /*
    965  * clear_scores:
    966  *	Clear out the scores so the next session start clean
    967  */
    968 static void
    969 clear_scores()
    970 {
    971 	IDENT	*ip, *nextip;
    972 
    973 	for (ip = Scores; ip != NULL; ip = nextip) {
    974 		nextip = ip->i_next;
    975 		(void) free((char *) ip);
    976 	}
    977 	Scores = NULL;
    978 }
    979