1*978477fcSdholland /* $NetBSD: playit.c,v 1.22 2014/03/30 05:38:13 dholland Exp $ */ 24052d13dSmrg /* 311967175Swiz * Copyright (c) 1983-2003, Regents of the University of California. 411967175Swiz * All rights reserved. 511967175Swiz * 611967175Swiz * Redistribution and use in source and binary forms, with or without 711967175Swiz * modification, are permitted provided that the following conditions are 811967175Swiz * met: 911967175Swiz * 1011967175Swiz * + Redistributions of source code must retain the above copyright 1111967175Swiz * notice, this list of conditions and the following disclaimer. 1211967175Swiz * + Redistributions in binary form must reproduce the above copyright 1311967175Swiz * notice, this list of conditions and the following disclaimer in the 1411967175Swiz * documentation and/or other materials provided with the distribution. 1511967175Swiz * + Neither the name of the University of California, San Francisco nor 1611967175Swiz * the names of its contributors may be used to endorse or promote 1711967175Swiz * products derived from this software without specific prior written 1811967175Swiz * permission. 1911967175Swiz * 2011967175Swiz * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 2111967175Swiz * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2211967175Swiz * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 2311967175Swiz * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2411967175Swiz * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2511967175Swiz * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2611967175Swiz * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2711967175Swiz * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2811967175Swiz * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2911967175Swiz * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3011967175Swiz * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 314052d13dSmrg */ 324052d13dSmrg 33544a5e33Slukem #include <sys/cdefs.h> 34544a5e33Slukem #ifndef lint 35*978477fcSdholland __RCSID("$NetBSD: playit.c,v 1.22 2014/03/30 05:38:13 dholland Exp $"); 36544a5e33Slukem #endif /* not lint */ 37544a5e33Slukem 38544a5e33Slukem #include <sys/file.h> 39e92308f2Smycroft #include <sys/poll.h> 40ff943cc8Slukem #include <err.h> 41544a5e33Slukem #include <errno.h> 424052d13dSmrg #include <curses.h> 434052d13dSmrg #include <ctype.h> 444052d13dSmrg #include <signal.h> 4509d8e77cSdholland #include <string.h> 46544a5e33Slukem #include <termios.h> 47544a5e33Slukem #include <unistd.h> 4809d8e77cSdholland 4909d8e77cSdholland #include "hunt_common.h" 5009d8e77cSdholland #include "hunt_private.h" 514052d13dSmrg 524052d13dSmrg #ifndef FREAD 534052d13dSmrg #define FREAD 1 544052d13dSmrg #endif 554052d13dSmrg 564052d13dSmrg #define clear_eol() clrtoeol() 574052d13dSmrg #define put_ch addch 584052d13dSmrg #define put_str addstr 594052d13dSmrg 604052d13dSmrg static int nchar_send; 614052d13dSmrg #ifdef OTTO 624052d13dSmrg int Otto_count; 634052d13dSmrg int Otto_mode; 644052d13dSmrg static int otto_y, otto_x; 654052d13dSmrg static char otto_face; 664052d13dSmrg #endif 674052d13dSmrg 684052d13dSmrg #define MAX_SEND 5 694052d13dSmrg 704052d13dSmrg /* 714052d13dSmrg * ibuf is the input buffer used for the stream from the driver. 724052d13dSmrg * It is small because we do not check for user input when there 734052d13dSmrg * are characters in the input buffer. 744052d13dSmrg */ 754052d13dSmrg static int icnt = 0; 764052d13dSmrg static unsigned char ibuf[256], *iptr = ibuf; 774052d13dSmrg 784052d13dSmrg #define GETCHR() (--icnt < 0 ? getchr() : *iptr++) 794052d13dSmrg 80cb5fd834Sjsm static unsigned char getchr(void); 81cb5fd834Sjsm static void send_stuff(void); 8275b3905dSdholland static void redraw_screen(void); 83544a5e33Slukem 844052d13dSmrg /* 854052d13dSmrg * playit: 864052d13dSmrg * Play a given game, handling all the curses commands from 874052d13dSmrg * the driver. 884052d13dSmrg */ 89544a5e33Slukem void 90fbca3d8cSdholland playit(void) 914052d13dSmrg { 92544a5e33Slukem int ch; 93544a5e33Slukem int y, x; 949ac5061bSdholland uint32_t version; 95a4a1b9dcSdholland ssize_t result; 964052d13dSmrg 97a4a1b9dcSdholland result = read(huntsocket, &version, sizeof(version)); 98a4a1b9dcSdholland if (result != (ssize_t)sizeof(version)) { 994052d13dSmrg bad_con(); 1004052d13dSmrg /* NOTREACHED */ 1014052d13dSmrg } 1029ac5061bSdholland if (ntohl(version) != (uint32_t)HUNT_VERSION) { 1034052d13dSmrg bad_ver(); 1044052d13dSmrg /* NOTREACHED */ 1054052d13dSmrg } 1064052d13dSmrg errno = 0; 1074052d13dSmrg #ifdef OTTO 1084052d13dSmrg Otto_count = 0; 1094052d13dSmrg #endif 1104052d13dSmrg nchar_send = MAX_SEND; 1114052d13dSmrg while ((ch = GETCHR()) != EOF) { 1124052d13dSmrg #ifdef DEBUG 1134052d13dSmrg fputc(ch, stderr); 1144052d13dSmrg #endif 1154052d13dSmrg switch (ch & 0377) { 1164052d13dSmrg case MOVE: 1174052d13dSmrg y = GETCHR(); 1184052d13dSmrg x = GETCHR(); 1194052d13dSmrg move(y, x); 1204052d13dSmrg break; 1214052d13dSmrg case ADDCH: 1224052d13dSmrg ch = GETCHR(); 1234052d13dSmrg #ifdef OTTO 1244052d13dSmrg switch (ch) { 1254052d13dSmrg 1264052d13dSmrg case '<': 1274052d13dSmrg case '>': 1284052d13dSmrg case '^': 1294052d13dSmrg case 'v': 1304052d13dSmrg otto_face = ch; 1314052d13dSmrg getyx(stdscr, otto_y, otto_x); 1324052d13dSmrg break; 1334052d13dSmrg } 1344052d13dSmrg #endif 1354052d13dSmrg put_ch(ch); 1364052d13dSmrg break; 1374052d13dSmrg case CLRTOEOL: 1384052d13dSmrg clear_eol(); 1394052d13dSmrg break; 1404052d13dSmrg case CLEAR: 1414052d13dSmrg clear_the_screen(); 1424052d13dSmrg break; 1434052d13dSmrg case REFRESH: 1444052d13dSmrg refresh(); 1454052d13dSmrg break; 1464052d13dSmrg case REDRAW: 1474052d13dSmrg redraw_screen(); 1484052d13dSmrg refresh(); 1494052d13dSmrg break; 1504052d13dSmrg case ENDWIN: 1514052d13dSmrg refresh(); 1524052d13dSmrg if ((ch = GETCHR()) == LAST_PLAYER) 153276cb4ddSdholland Last_player = true; 1544052d13dSmrg ch = EOF; 1554052d13dSmrg goto out; 1564052d13dSmrg case BELL: 1574052d13dSmrg beep(); 1584052d13dSmrg break; 1594052d13dSmrg case READY: 1604052d13dSmrg refresh(); 1614052d13dSmrg if (nchar_send < 0) 162*978477fcSdholland tcflush(STDIN_FILENO, TCIFLUSH); 1634052d13dSmrg nchar_send = MAX_SEND; 1644052d13dSmrg #ifndef OTTO 1654052d13dSmrg (void) GETCHR(); 1664052d13dSmrg #else 1674052d13dSmrg Otto_count -= (GETCHR() & 0xff); 1684052d13dSmrg if (!Am_monitor) { 1694052d13dSmrg #ifdef DEBUG 1704052d13dSmrg fputc('0' + Otto_count, stderr); 1714052d13dSmrg #endif 1724052d13dSmrg if (Otto_count == 0 && Otto_mode) 1734052d13dSmrg otto(otto_y, otto_x, otto_face); 1744052d13dSmrg } 1754052d13dSmrg #endif 1764052d13dSmrg break; 1774052d13dSmrg default: 1784052d13dSmrg #ifdef OTTO 1794052d13dSmrg switch (ch) { 1804052d13dSmrg 1814052d13dSmrg case '<': 1824052d13dSmrg case '>': 1834052d13dSmrg case '^': 1844052d13dSmrg case 'v': 1854052d13dSmrg otto_face = ch; 1864052d13dSmrg getyx(stdscr, otto_y, otto_x); 1874052d13dSmrg break; 1884052d13dSmrg } 1894052d13dSmrg #endif 1904052d13dSmrg put_ch(ch); 1914052d13dSmrg break; 1924052d13dSmrg } 1934052d13dSmrg } 1944052d13dSmrg out: 195ca5d861dSdholland (void) close(huntsocket); 1964052d13dSmrg } 1974052d13dSmrg 1984052d13dSmrg /* 1994052d13dSmrg * getchr: 2004052d13dSmrg * Grab input and pass it along to the driver 2014052d13dSmrg * Return any characters from the driver 2024052d13dSmrg * When this routine is called by GETCHR, we already know there are 2034052d13dSmrg * no characters in the input buffer. 2044052d13dSmrg */ 205544a5e33Slukem static unsigned char 206fbca3d8cSdholland getchr(void) 2074052d13dSmrg { 208e92308f2Smycroft struct pollfd set[2]; 209e92308f2Smycroft int nfds; 2104052d13dSmrg 211ca5d861dSdholland set[0].fd = huntsocket; 212e92308f2Smycroft set[0].events = POLLIN; 213*978477fcSdholland set[1].fd = STDIN_FILENO; 214e92308f2Smycroft set[1].events = POLLIN; 2154052d13dSmrg 2164052d13dSmrg one_more_time: 2174052d13dSmrg do { 2184052d13dSmrg errno = 0; 219e92308f2Smycroft nfds = poll(set, 2, INFTIM); 2204052d13dSmrg } while (nfds <= 0 && errno == EINTR); 2214052d13dSmrg 222e92308f2Smycroft if (set[1].revents && POLLIN) 2234052d13dSmrg send_stuff(); 224e92308f2Smycroft if (! (set[0].revents & POLLIN)) 2254052d13dSmrg goto one_more_time; 226ca5d861dSdholland icnt = read(huntsocket, ibuf, sizeof ibuf); 2274052d13dSmrg if (icnt < 0) { 2284052d13dSmrg bad_con(); 2294052d13dSmrg /* NOTREACHED */ 2304052d13dSmrg } 2314052d13dSmrg if (icnt == 0) 2324052d13dSmrg goto one_more_time; 2334052d13dSmrg iptr = ibuf; 2344052d13dSmrg icnt--; 2354052d13dSmrg return *iptr++; 2364052d13dSmrg } 2374052d13dSmrg 2384052d13dSmrg /* 2394052d13dSmrg * send_stuff: 2404052d13dSmrg * Send standard input characters to the driver 2414052d13dSmrg */ 242544a5e33Slukem static void 243fbca3d8cSdholland send_stuff(void) 2444052d13dSmrg { 245544a5e33Slukem int count; 246544a5e33Slukem char *sp, *nsp; 2474052d13dSmrg static char inp[sizeof Buf]; 2484052d13dSmrg 249*978477fcSdholland count = read(STDIN_FILENO, Buf, sizeof Buf); 2504052d13dSmrg if (count <= 0) 2514052d13dSmrg return; 2524052d13dSmrg if (nchar_send <= 0 && !no_beep) { 2534052d13dSmrg (void) write(1, "\7", 1); /* CTRL('G') */ 2544052d13dSmrg return; 2554052d13dSmrg } 2564052d13dSmrg 2574052d13dSmrg /* 2584052d13dSmrg * look for 'q'uit commands; if we find one, 2594052d13dSmrg * confirm it. If it is not confirmed, strip 2604052d13dSmrg * it out of the input 2614052d13dSmrg */ 2624052d13dSmrg Buf[count] = '\0'; 2634052d13dSmrg nsp = inp; 2644052d13dSmrg for (sp = Buf; *sp != '\0'; sp++) 26550b357ccSdholland if ((*nsp = map_key[(unsigned char)*sp]) == 'q') 266544a5e33Slukem intr(0); 2674052d13dSmrg else 2684052d13dSmrg nsp++; 2694052d13dSmrg count = nsp - inp; 2704052d13dSmrg if (count) { 2714052d13dSmrg #ifdef OTTO 2724052d13dSmrg Otto_count += count; 2734052d13dSmrg #endif 2744052d13dSmrg nchar_send -= count; 2754052d13dSmrg if (nchar_send < 0) 2764052d13dSmrg count += nchar_send; 277ca5d861dSdholland (void) write(huntsocket, inp, count); 2784052d13dSmrg } 2794052d13dSmrg } 2804052d13dSmrg 2814052d13dSmrg /* 2824052d13dSmrg * quit: 2834052d13dSmrg * Handle the end of the game when the player dies 2844052d13dSmrg */ 285544a5e33Slukem int 286fbca3d8cSdholland quit(int old_status) 2874052d13dSmrg { 288276cb4ddSdholland bool explain; 289276cb4ddSdholland int ch; 2904052d13dSmrg 2914052d13dSmrg if (Last_player) 2924052d13dSmrg return Q_QUIT; 2934052d13dSmrg #ifdef OTTO 2944052d13dSmrg if (Otto_mode) 2954052d13dSmrg return Q_CLOAK; 2964052d13dSmrg #endif 2974052d13dSmrg move(HEIGHT, 0); 2984052d13dSmrg put_str("Re-enter game [ynwo]? "); 2994052d13dSmrg clear_eol(); 300276cb4ddSdholland explain = false; 3014052d13dSmrg for (;;) { 3024052d13dSmrg refresh(); 3034052d13dSmrg if (isupper(ch = getchar())) 3044052d13dSmrg ch = tolower(ch); 3054052d13dSmrg if (ch == 'y') 3064052d13dSmrg return old_status; 3074052d13dSmrg else if (ch == 'o') 3084052d13dSmrg break; 3094052d13dSmrg else if (ch == 'n') { 3104052d13dSmrg #ifndef INTERNET 3114052d13dSmrg return Q_QUIT; 3124052d13dSmrg #else 3134052d13dSmrg move(HEIGHT, 0); 3144052d13dSmrg put_str("Write a parting message [yn]? "); 3154052d13dSmrg clear_eol(); 3164052d13dSmrg refresh(); 3174052d13dSmrg for (;;) { 3184052d13dSmrg if (isupper(ch = getchar())) 3194052d13dSmrg ch = tolower(ch); 3204052d13dSmrg if (ch == 'y') 3214052d13dSmrg goto get_message; 3224052d13dSmrg if (ch == 'n') 3234052d13dSmrg return Q_QUIT; 3244052d13dSmrg } 3254052d13dSmrg #endif 3264052d13dSmrg } 3274052d13dSmrg #ifdef INTERNET 3284052d13dSmrg else if (ch == 'w') { 3294052d13dSmrg static char buf[WIDTH + WIDTH % 2]; 3304052d13dSmrg char *cp, c; 3314052d13dSmrg 3324052d13dSmrg get_message: 3334052d13dSmrg c = ch; /* save how we got here */ 3344052d13dSmrg move(HEIGHT, 0); 3354052d13dSmrg put_str("Message: "); 3364052d13dSmrg clear_eol(); 3374052d13dSmrg refresh(); 3384052d13dSmrg cp = buf; 3394052d13dSmrg for (;;) { 3404052d13dSmrg refresh(); 3414052d13dSmrg if ((ch = getchar()) == '\n' || ch == '\r') 3424052d13dSmrg break; 34381eafd78Sdholland if (ch == erasechar()) { 3444052d13dSmrg if (cp > buf) { 3454052d13dSmrg int y, x; 3464052d13dSmrg getyx(stdscr, y, x); 3474052d13dSmrg move(y, x - 1); 3484052d13dSmrg cp -= 1; 3494052d13dSmrg clear_eol(); 3504052d13dSmrg } 3514052d13dSmrg continue; 3524052d13dSmrg } 35381eafd78Sdholland else if (ch == killchar()) { 3544052d13dSmrg int y, x; 3554052d13dSmrg getyx(stdscr, y, x); 3564052d13dSmrg move(y, x - (cp - buf)); 3574052d13dSmrg cp = buf; 3584052d13dSmrg clear_eol(); 3594052d13dSmrg continue; 3604052d13dSmrg } else if (!isprint(ch)) { 3614052d13dSmrg beep(); 3624052d13dSmrg continue; 3634052d13dSmrg } 3644052d13dSmrg put_ch(ch); 3654052d13dSmrg *cp++ = ch; 3664052d13dSmrg if (cp + 1 >= buf + sizeof buf) 3674052d13dSmrg break; 3684052d13dSmrg } 3694052d13dSmrg *cp = '\0'; 3704052d13dSmrg Send_message = buf; 3714052d13dSmrg return (c == 'w') ? old_status : Q_MESSAGE; 3724052d13dSmrg } 3734052d13dSmrg #endif 3744052d13dSmrg beep(); 3754052d13dSmrg if (!explain) { 3764052d13dSmrg put_str("(Yes, No, Write message, or Options) "); 377276cb4ddSdholland explain = true; 3784052d13dSmrg } 3794052d13dSmrg } 3804052d13dSmrg 3814052d13dSmrg move(HEIGHT, 0); 3824052d13dSmrg #ifdef FLY 3834052d13dSmrg put_str("Scan, Cloak, Flying, or Quit? "); 3844052d13dSmrg #else 3854052d13dSmrg put_str("Scan, Cloak, or Quit? "); 3864052d13dSmrg #endif 3874052d13dSmrg clear_eol(); 3884052d13dSmrg refresh(); 389276cb4ddSdholland explain = false; 3904052d13dSmrg for (;;) { 3914052d13dSmrg if (isupper(ch = getchar())) 3924052d13dSmrg ch = tolower(ch); 3934052d13dSmrg if (ch == 's') 3944052d13dSmrg return Q_SCAN; 3954052d13dSmrg else if (ch == 'c') 3964052d13dSmrg return Q_CLOAK; 3974052d13dSmrg #ifdef FLY 3984052d13dSmrg else if (ch == 'f') 3994052d13dSmrg return Q_FLY; 4004052d13dSmrg #endif 4014052d13dSmrg else if (ch == 'q') 4024052d13dSmrg return Q_QUIT; 4034052d13dSmrg beep(); 4044052d13dSmrg if (!explain) { 4054052d13dSmrg #ifdef FLY 4064052d13dSmrg put_str("[SCFQ] "); 4074052d13dSmrg #else 4084052d13dSmrg put_str("[SCQ] "); 4094052d13dSmrg #endif 410276cb4ddSdholland explain = true; 4114052d13dSmrg } 4124052d13dSmrg refresh(); 4134052d13dSmrg } 4144052d13dSmrg } 4154052d13dSmrg 416544a5e33Slukem void 417fbca3d8cSdholland clear_the_screen(void) 4184052d13dSmrg { 4194052d13dSmrg clear(); 4204052d13dSmrg move(0, 0); 4214052d13dSmrg refresh(); 4224052d13dSmrg } 4234052d13dSmrg 42475b3905dSdholland static void 425fbca3d8cSdholland redraw_screen(void) 4264052d13dSmrg { 4274052d13dSmrg clearok(stdscr, TRUE); 4284052d13dSmrg touchwin(stdscr); 4294052d13dSmrg } 4304052d13dSmrg 4314052d13dSmrg /* 4324052d13dSmrg * do_message: 4334052d13dSmrg * Send a message to the driver and return 4344052d13dSmrg */ 435544a5e33Slukem void 436fbca3d8cSdholland do_message(void) 4374052d13dSmrg { 4389ac5061bSdholland uint32_t version; 439a4a1b9dcSdholland ssize_t result; 4404052d13dSmrg 441a4a1b9dcSdholland result = read(huntsocket, &version, sizeof(version)); 442a4a1b9dcSdholland if (result != (ssize_t)sizeof(version)) { 4434052d13dSmrg bad_con(); 4444052d13dSmrg /* NOTREACHED */ 4454052d13dSmrg } 4469ac5061bSdholland if (ntohl(version) != (uint32_t)HUNT_VERSION) { 4474052d13dSmrg bad_ver(); 4484052d13dSmrg /* NOTREACHED */ 4494052d13dSmrg } 4504052d13dSmrg #ifdef INTERNET 451ca5d861dSdholland if (write(huntsocket, Send_message, strlen(Send_message)) < 0) { 4524052d13dSmrg bad_con(); 4534052d13dSmrg /* NOTREACHED */ 4544052d13dSmrg } 4554052d13dSmrg #endif 456ca5d861dSdholland (void) close(huntsocket); 4574052d13dSmrg } 458