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