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