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