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