xref: /netbsd/games/hunt/hunt/playit.c (revision 6db346a2)
1 /*	$NetBSD: playit.c,v 1.25 2014/03/30 05:48:35 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: playit.c,v 1.25 2014/03/30 05:48:35 dholland Exp $");
36 #endif /* not lint */
37 
38 #include <sys/file.h>
39 #include <sys/poll.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <curses.h>
43 #include <ctype.h>
44 #include <signal.h>
45 #include <string.h>
46 #include <termios.h>
47 #include <unistd.h>
48 
49 #include "hunt_common.h"
50 #include "hunt_private.h"
51 
52 #ifndef FREAD
53 #define FREAD	1
54 #endif
55 
56 
57 static int nchar_send;
58 #ifdef OTTO
59 int Otto_count;
60 int Otto_mode;
61 static int otto_y, otto_x;
62 static char otto_face;
63 #endif
64 
65 #define MAX_SEND	5
66 
67 /*
68  * ibuf is the input buffer used for the stream from the driver.
69  * It is small because we do not check for user input when there
70  * are characters in the input buffer.
71  */
72 static int icnt = 0;
73 static unsigned char ibuf[256], *iptr = ibuf;
74 
75 #define GETCHR()	(--icnt < 0 ? getchr() : *iptr++)
76 
77 static unsigned char getchr(void);
78 static void send_stuff(void);
79 static void redraw_screen(void);
80 
81 /*
82  * playit:
83  *	Play a given game, handling all the curses commands from
84  *	the driver.
85  */
86 void
playit(void)87 playit(void)
88 {
89 	int ch;
90 	int y, x;
91 	uint32_t version;
92 	ssize_t result;
93 
94 	result = read(huntsocket, &version, sizeof(version));
95 	if (result != (ssize_t)sizeof(version)) {
96 		bad_con();
97 		/* NOTREACHED */
98 	}
99 	if (ntohl(version) != (uint32_t)HUNT_VERSION) {
100 		bad_ver();
101 		/* NOTREACHED */
102 	}
103 	errno = 0;
104 #ifdef OTTO
105 	Otto_count = 0;
106 #endif
107 	nchar_send = MAX_SEND;
108 	while ((ch = GETCHR()) != EOF) {
109 #ifdef DEBUG
110 		fputc(ch, stderr);
111 #endif
112 		switch (ch & 0377) {
113 		  case MOVE:
114 			y = GETCHR();
115 			x = GETCHR();
116 			move(y, x);
117 			break;
118 		  case ADDCH:
119 			ch = GETCHR();
120 #ifdef OTTO
121 			switch (ch) {
122 
123 			case '<':
124 			case '>':
125 			case '^':
126 			case 'v':
127 				otto_face = ch;
128 				getyx(stdscr, otto_y, otto_x);
129 				break;
130 			}
131 #endif
132 			addch(ch);
133 			break;
134 		  case CLRTOEOL:
135 			clrtoeol();
136 			break;
137 		  case CLEAR:
138 			clear_the_screen();
139 			break;
140 		  case REFRESH:
141 			refresh();
142 			break;
143 		  case REDRAW:
144 			redraw_screen();
145 			refresh();
146 			break;
147 		  case ENDWIN:
148 			refresh();
149 			if ((ch = GETCHR()) == LAST_PLAYER)
150 				Last_player = true;
151 			ch = EOF;
152 			goto out;
153 		  case BELL:
154 			beep();
155 			break;
156 		  case READY:
157 			refresh();
158 			if (nchar_send < 0)
159 				tcflush(STDIN_FILENO, TCIFLUSH);
160 			nchar_send = MAX_SEND;
161 #ifndef OTTO
162 			(void) GETCHR();
163 #else
164 			Otto_count -= (GETCHR() & 0xff);
165 			if (!Am_monitor) {
166 #ifdef DEBUG
167 				fputc('0' + Otto_count, stderr);
168 #endif
169 				if (Otto_count == 0 && Otto_mode)
170 					otto(otto_y, otto_x, otto_face);
171 			}
172 #endif
173 			break;
174 		  default:
175 #ifdef OTTO
176 			switch (ch) {
177 
178 			case '<':
179 			case '>':
180 			case '^':
181 			case 'v':
182 				otto_face = ch;
183 				getyx(stdscr, otto_y, otto_x);
184 				break;
185 			}
186 #endif
187 			addch(ch);
188 			break;
189 		}
190 	}
191 out:
192 	(void) close(huntsocket);
193 }
194 
195 /*
196  * getchr:
197  *	Grab input and pass it along to the driver
198  *	Return any characters from the driver
199  *	When this routine is called by GETCHR, we already know there are
200  *	no characters in the input buffer.
201  */
202 static unsigned char
getchr(void)203 getchr(void)
204 {
205 	struct pollfd set[2];
206 	int nfds;
207 
208 	set[0].fd = huntsocket;
209 	set[0].events = POLLIN;
210 	set[1].fd = STDIN_FILENO;
211 	set[1].events = POLLIN;
212 
213 one_more_time:
214 	do {
215 		errno = 0;
216 		nfds = poll(set, 2, INFTIM);
217 	} while (nfds <= 0 && errno == EINTR);
218 
219 	if (set[1].revents && POLLIN)
220 		send_stuff();
221 	if (! (set[0].revents & POLLIN))
222 		goto one_more_time;
223 	icnt = read(huntsocket, ibuf, sizeof ibuf);
224 	if (icnt < 0) {
225 		bad_con();
226 		/* NOTREACHED */
227 	}
228 	if (icnt == 0)
229 		goto one_more_time;
230 	iptr = ibuf;
231 	icnt--;
232 	return *iptr++;
233 }
234 
235 /*
236  * send_stuff:
237  *	Send standard input characters to the driver
238  */
239 static void
send_stuff(void)240 send_stuff(void)
241 {
242 	int count;
243 	char *sp, *nsp;
244 	static char inp[sizeof Buf];
245 
246 	count = read(STDIN_FILENO, Buf, sizeof(Buf) - 1);
247 	if (count <= 0)
248 		return;
249 	if (nchar_send <= 0 && !no_beep) {
250 		(void) beep();
251 		return;
252 	}
253 
254 	/*
255 	 * look for 'q'uit commands; if we find one,
256 	 * confirm it.  If it is not confirmed, strip
257 	 * it out of the input
258 	 */
259 	Buf[count] = '\0';
260 	nsp = inp;
261 	for (sp = Buf; *sp != '\0'; sp++)
262 		if ((*nsp = map_key[(unsigned char)*sp]) == 'q')
263 			intr(0);
264 		else
265 			nsp++;
266 	count = nsp - inp;
267 	if (count) {
268 #ifdef OTTO
269 		Otto_count += count;
270 #endif
271 		nchar_send -= count;
272 		if (nchar_send < 0)
273 			count += nchar_send;
274 		(void) write(huntsocket, inp, count);
275 	}
276 }
277 
278 /*
279  * quit:
280  *	Handle the end of the game when the player dies
281  */
282 int
quit(int old_status)283 quit(int old_status)
284 {
285 	bool explain;
286 	int ch;
287 
288 	if (Last_player)
289 		return Q_QUIT;
290 #ifdef OTTO
291 	if (Otto_mode)
292 		return Q_CLOAK;
293 #endif
294 	move(HEIGHT, 0);
295 	addstr("Re-enter game [ynwo]? ");
296 	clrtoeol();
297 	explain = false;
298 	for (;;) {
299 		refresh();
300 		if (isupper(ch = getchar()))
301 			ch = tolower(ch);
302 		if (ch == 'y')
303 			return old_status;
304 		else if (ch == 'o')
305 			break;
306 		else if (ch == 'n') {
307 #ifndef INTERNET
308 			return Q_QUIT;
309 #else
310 			move(HEIGHT, 0);
311 			addstr("Write a parting message [yn]? ");
312 			clrtoeol();
313 			refresh();
314 			for (;;) {
315 				if (isupper(ch = getchar()))
316 					ch = tolower(ch);
317 				if (ch == 'y')
318 					goto get_message;
319 				if (ch == 'n')
320 					return Q_QUIT;
321 			}
322 #endif
323 		}
324 #ifdef INTERNET
325 		else if (ch == 'w') {
326 			static char buf[WIDTH + WIDTH % 2];
327 			char *cp, c;
328 
329 get_message:
330 			c = ch;		/* save how we got here */
331 			move(HEIGHT, 0);
332 			addstr("Message: ");
333 			clrtoeol();
334 			refresh();
335 			cp = buf;
336 			for (;;) {
337 				refresh();
338 				if ((ch = getchar()) == '\n' || ch == '\r')
339 					break;
340 				if (ch == erasechar()) {
341 					if (cp > buf) {
342 						int y, x;
343 						getyx(stdscr, y, x);
344 						move(y, x - 1);
345 						cp -= 1;
346 						clrtoeol();
347 					}
348 					continue;
349 				}
350 				else if (ch == killchar()) {
351 					int y, x;
352 					getyx(stdscr, y, x);
353 					move(y, x - (cp - buf));
354 					cp = buf;
355 					clrtoeol();
356 					continue;
357 				} else if (!isprint(ch)) {
358 					beep();
359 					continue;
360 				}
361 				addch(ch);
362 				*cp++ = ch;
363 				if (cp + 1 >= buf + sizeof buf)
364 					break;
365 			}
366 			*cp = '\0';
367 			Send_message = buf;
368 			return (c == 'w') ? old_status : Q_MESSAGE;
369 		}
370 #endif
371 		beep();
372 		if (!explain) {
373 			addstr("(Yes, No, Write message, or Options) ");
374 			explain = true;
375 		}
376 	}
377 
378 	move(HEIGHT, 0);
379 #ifdef FLY
380 	addstr("Scan, Cloak, Flying, or Quit? ");
381 #else
382 	addstr("Scan, Cloak, or Quit? ");
383 #endif
384 	clrtoeol();
385 	refresh();
386 	explain = false;
387 	for (;;) {
388 		if (isupper(ch = getchar()))
389 			ch = tolower(ch);
390 		if (ch == 's')
391 			return Q_SCAN;
392 		else if (ch == 'c')
393 			return Q_CLOAK;
394 #ifdef FLY
395 		else if (ch == 'f')
396 			return Q_FLY;
397 #endif
398 		else if (ch == 'q')
399 			return Q_QUIT;
400 		beep();
401 		if (!explain) {
402 #ifdef FLY
403 			addstr("[SCFQ] ");
404 #else
405 			addstr("[SCQ] ");
406 #endif
407 			explain = true;
408 		}
409 		refresh();
410 	}
411 }
412 
413 void
clear_the_screen(void)414 clear_the_screen(void)
415 {
416 	clear();
417 	move(0, 0);
418 	refresh();
419 }
420 
421 static void
redraw_screen(void)422 redraw_screen(void)
423 {
424 	clearok(stdscr, TRUE);
425 	touchwin(stdscr);
426 }
427 
428 /*
429  * do_message:
430  *	Send a message to the driver and return
431  */
432 void
do_message(void)433 do_message(void)
434 {
435 	uint32_t version;
436 	ssize_t result;
437 
438 	result = read(huntsocket, &version, sizeof(version));
439 	if (result != (ssize_t)sizeof(version)) {
440 		bad_con();
441 		/* NOTREACHED */
442 	}
443 	if (ntohl(version) != (uint32_t)HUNT_VERSION) {
444 		bad_ver();
445 		/* NOTREACHED */
446 	}
447 #ifdef INTERNET
448 	if (write(huntsocket, Send_message, strlen(Send_message)) < 0) {
449 		bad_con();
450 		/* NOTREACHED */
451 	}
452 #endif
453 	(void) close(huntsocket);
454 }
455