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