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