xref: /netbsd/games/hunt/hunt/playit.c (revision 978477fc)
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