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