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