xref: /netbsd/games/hunt/hunt/playit.c (revision c4a72b64)
1 /*	$NetBSD: playit.c,v 1.5 2002/09/20 15:47:19 mycroft Exp $	*/
2 /*
3  *  Hunt
4  *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
5  *  San Francisco, California
6  */
7 
8 #include <sys/cdefs.h>
9 #ifndef lint
10 __RCSID("$NetBSD: playit.c,v 1.5 2002/09/20 15:47:19 mycroft Exp $");
11 #endif /* not lint */
12 
13 # include	<sys/file.h>
14 # include	<sys/poll.h>
15 # include	<err.h>
16 # include	<errno.h>
17 # include	<curses.h>
18 # include	<ctype.h>
19 # include	<signal.h>
20 # if defined(HPUX) || (defined(BSD_RELEASE) && BSD_RELEASE >= 44)
21 # include	<termios.h>
22 # include	<unistd.h>
23 # endif
24 # include	"hunt.h"
25 
26 # ifndef FREAD
27 # define	FREAD	1
28 # endif
29 
30 # if !defined(USE_CURSES) || !defined(TERMINFO)
31 # define	beep()		(void) putchar(CTRL('G'))
32 # endif
33 # if !defined(USE_CURSES)
34 # undef		refresh
35 # define	refresh()	(void) fflush(stdout);
36 # endif
37 # ifdef USE_CURSES
38 # define	clear_eol()	clrtoeol()
39 # define	put_ch		addch
40 # define	put_str		addstr
41 # endif
42 
43 static int	nchar_send;
44 # ifndef USE_CURSES
45 char		screen[SCREEN_HEIGHT][SCREEN_WIDTH2], blanks[SCREEN_WIDTH];
46 int		cur_row, cur_col;
47 # endif
48 # ifdef OTTO
49 int		Otto_count;
50 int		Otto_mode;
51 static int	otto_y, otto_x;
52 static char	otto_face;
53 # endif
54 
55 # define	MAX_SEND	5
56 # define	STDIN		0
57 
58 /*
59  * ibuf is the input buffer used for the stream from the driver.
60  * It is small because we do not check for user input when there
61  * are characters in the input buffer.
62  */
63 static int		icnt = 0;
64 static unsigned char	ibuf[256], *iptr = ibuf;
65 
66 #define	GETCHR()	(--icnt < 0 ? getchr() : *iptr++)
67 
68 #if !defined(BSD_RELEASE) || BSD_RELEASE < 44
69 extern int	_putchar();
70 #endif
71 
72 static	unsigned char	getchr __P((void));
73 static	void		send_stuff __P((void));
74 
75 /*
76  * playit:
77  *	Play a given game, handling all the curses commands from
78  *	the driver.
79  */
80 void
81 playit()
82 {
83 	int		ch;
84 	int		y, x;
85 	long		version;
86 
87 	if (read(Socket, (char *) &version, LONGLEN) != LONGLEN) {
88 		bad_con();
89 		/* NOTREACHED */
90 	}
91 	if (ntohl(version) != HUNT_VERSION) {
92 		bad_ver();
93 		/* NOTREACHED */
94 	}
95 	errno = 0;
96 # ifdef OTTO
97 	Otto_count = 0;
98 # endif
99 	nchar_send = MAX_SEND;
100 	while ((ch = GETCHR()) != EOF) {
101 # ifdef DEBUG
102 		fputc(ch, stderr);
103 # endif
104 		switch (ch & 0377) {
105 		  case MOVE:
106 			y = GETCHR();
107 			x = GETCHR();
108 # ifdef USE_CURSES
109 			move(y, x);
110 # else
111 			mvcur(cur_row, cur_col, y, x);
112 			cur_row = y;
113 			cur_col = x;
114 # endif
115 			break;
116 		  case ADDCH:
117 			ch = GETCHR();
118 # ifdef OTTO
119 			switch (ch) {
120 
121 			case '<':
122 			case '>':
123 			case '^':
124 			case 'v':
125 				otto_face = ch;
126 # ifdef USE_CURSES
127 				getyx(stdscr, otto_y, otto_x);
128 # else
129 				otto_y = cur_row;
130 				otto_x = cur_col;
131 # endif
132 				break;
133 			}
134 # endif
135 			put_ch(ch);
136 			break;
137 		  case CLRTOEOL:
138 			clear_eol();
139 			break;
140 		  case CLEAR:
141 			clear_the_screen();
142 			break;
143 		  case REFRESH:
144 			refresh();
145 			break;
146 		  case REDRAW:
147 			redraw_screen();
148 			refresh();
149 			break;
150 		  case ENDWIN:
151 			refresh();
152 			if ((ch = GETCHR()) == LAST_PLAYER)
153 				Last_player = TRUE;
154 			ch = EOF;
155 			goto out;
156 		  case BELL:
157 			beep();
158 			break;
159 		  case READY:
160 			refresh();
161 			if (nchar_send < 0)
162 # if defined(HPUX) || (defined(BSD_RELEASE) && BSD_RELEASE >= 44)
163 				tcflush(STDIN, TCIFLUSH);
164 # else
165 # ifndef TCFLSH
166 				(void) ioctl(STDIN, TIOCFLUSH, &in);
167 # else
168 				(void) ioctl(STDIN, TCFLSH, 0);
169 # endif
170 # endif
171 			nchar_send = MAX_SEND;
172 # ifndef OTTO
173 			(void) GETCHR();
174 # else
175 			Otto_count -= (GETCHR() & 0xff);
176 			if (!Am_monitor) {
177 # ifdef DEBUG
178 				fputc('0' + Otto_count, stderr);
179 # endif
180 				if (Otto_count == 0 && Otto_mode)
181 					otto(otto_y, otto_x, otto_face);
182 			}
183 # endif
184 			break;
185 		  default:
186 # ifdef OTTO
187 			switch (ch) {
188 
189 			case '<':
190 			case '>':
191 			case '^':
192 			case 'v':
193 				otto_face = ch;
194 # ifdef USE_CURSES
195 				getyx(stdscr, otto_y, otto_x);
196 # else
197 				otto_y = cur_row;
198 				otto_x = cur_col;
199 # endif
200 				break;
201 			}
202 # endif
203 			put_ch(ch);
204 			break;
205 		}
206 	}
207 out:
208 	(void) close(Socket);
209 }
210 
211 /*
212  * getchr:
213  *	Grab input and pass it along to the driver
214  *	Return any characters from the driver
215  *	When this routine is called by GETCHR, we already know there are
216  *	no characters in the input buffer.
217  */
218 static unsigned char
219 getchr()
220 {
221 	struct	pollfd set[2];
222 	int	nfds;
223 
224 	set[0].fd = Socket;
225 	set[0].events = POLLIN;
226 	set[1].fd = STDIN;
227 	set[1].events = POLLIN;
228 
229 one_more_time:
230 	do {
231 		errno = 0;
232 		nfds = poll(set, 2, INFTIM);
233 	} while (nfds <= 0 && errno == EINTR);
234 
235 	if (set[1].revents && POLLIN)
236 		send_stuff();
237 	if (! (set[0].revents & POLLIN))
238 		goto one_more_time;
239 	icnt = read(Socket, ibuf, sizeof ibuf);
240 	if (icnt < 0) {
241 		bad_con();
242 		/* NOTREACHED */
243 	}
244 	if (icnt == 0)
245 		goto one_more_time;
246 	iptr = ibuf;
247 	icnt--;
248 	return *iptr++;
249 }
250 
251 /*
252  * send_stuff:
253  *	Send standard input characters to the driver
254  */
255 static void
256 send_stuff()
257 {
258 	int		count;
259 	char		*sp, *nsp;
260 	static char	inp[sizeof Buf];
261 
262 	count = read(STDIN, Buf, sizeof Buf);
263 	if (count <= 0)
264 		return;
265 	if (nchar_send <= 0 && !no_beep) {
266 		(void) write(1, "\7", 1);	/* CTRL('G') */
267 		return;
268 	}
269 
270 	/*
271 	 * look for 'q'uit commands; if we find one,
272 	 * confirm it.  If it is not confirmed, strip
273 	 * it out of the input
274 	 */
275 	Buf[count] = '\0';
276 	nsp = inp;
277 	for (sp = Buf; *sp != '\0'; sp++)
278 		if ((*nsp = map_key[(int)*sp]) == 'q')
279 			intr(0);
280 		else
281 			nsp++;
282 	count = nsp - inp;
283 	if (count) {
284 # ifdef OTTO
285 		Otto_count += count;
286 # endif
287 		nchar_send -= count;
288 		if (nchar_send < 0)
289 			count += nchar_send;
290 		(void) write(Socket, inp, count);
291 	}
292 }
293 
294 /*
295  * quit:
296  *	Handle the end of the game when the player dies
297  */
298 int
299 quit(old_status)
300 	int	old_status;
301 {
302 	int	explain, ch;
303 
304 	if (Last_player)
305 		return Q_QUIT;
306 # ifdef OTTO
307 	if (Otto_mode)
308 		return Q_CLOAK;
309 # endif
310 # ifdef USE_CURSES
311 	move(HEIGHT, 0);
312 # else
313 	mvcur(cur_row, cur_col, HEIGHT, 0);
314 	cur_row = HEIGHT;
315 	cur_col = 0;
316 # endif
317 	put_str("Re-enter game [ynwo]? ");
318 	clear_eol();
319 	explain = FALSE;
320 	for (;;) {
321 		refresh();
322 		if (isupper(ch = getchar()))
323 			ch = tolower(ch);
324 		if (ch == 'y')
325 			return old_status;
326 		else if (ch == 'o')
327 			break;
328 		else if (ch == 'n') {
329 # ifndef INTERNET
330 			return Q_QUIT;
331 # else
332 # ifdef USE_CURSES
333 			move(HEIGHT, 0);
334 # else
335 			mvcur(cur_row, cur_col, HEIGHT, 0);
336 			cur_row = HEIGHT;
337 			cur_col = 0;
338 # endif
339 			put_str("Write a parting message [yn]? ");
340 			clear_eol();
341 			refresh();
342 			for (;;) {
343 				if (isupper(ch = getchar()))
344 					ch = tolower(ch);
345 				if (ch == 'y')
346 					goto get_message;
347 				if (ch == 'n')
348 					return Q_QUIT;
349 			}
350 # endif
351 		}
352 # ifdef INTERNET
353 		else if (ch == 'w') {
354 			static	char	buf[WIDTH + WIDTH % 2];
355 			char		*cp, c;
356 
357 get_message:
358 			c = ch;		/* save how we got here */
359 # ifdef USE_CURSES
360 			move(HEIGHT, 0);
361 # else
362 			mvcur(cur_row, cur_col, HEIGHT, 0);
363 			cur_row = HEIGHT;
364 			cur_col = 0;
365 # endif
366 			put_str("Message: ");
367 			clear_eol();
368 			refresh();
369 			cp = buf;
370 			for (;;) {
371 				refresh();
372 				if ((ch = getchar()) == '\n' || ch == '\r')
373 					break;
374 # if defined(TERMINFO) || BSD_RELEASE >= 44
375 				if (ch == erasechar())
376 # else
377 				if (ch == _tty.sg_erase)
378 # endif
379 				{
380 					if (cp > buf) {
381 # ifdef USE_CURSES
382 						int y, x;
383 						getyx(stdscr, y, x);
384 						move(y, x - 1);
385 # else
386 						mvcur(cur_row, cur_col, cur_row,
387 								cur_col - 1);
388 						cur_col -= 1;
389 # endif
390 						cp -= 1;
391 						clear_eol();
392 					}
393 					continue;
394 				}
395 # if defined(TERMINFO) || BSD_RELEASE >= 44
396 				else if (ch == killchar())
397 # else
398 				else if (ch == _tty.sg_kill)
399 # endif
400 				{
401 # ifdef USE_CURSES
402 					int y, x;
403 					getyx(stdscr, y, x);
404 					move(y, x - (cp - buf));
405 # else
406 					mvcur(cur_row, cur_col, cur_row,
407 							cur_col - (cp - buf));
408 					cur_col -= cp - buf;
409 # endif
410 					cp = buf;
411 					clear_eol();
412 					continue;
413 				} else if (!isprint(ch)) {
414 					beep();
415 					continue;
416 				}
417 				put_ch(ch);
418 				*cp++ = ch;
419 				if (cp + 1 >= buf + sizeof buf)
420 					break;
421 			}
422 			*cp = '\0';
423 			Send_message = buf;
424 			return (c == 'w') ? old_status : Q_MESSAGE;
425 		}
426 # endif
427 		beep();
428 		if (!explain) {
429 			put_str("(Yes, No, Write message, or Options) ");
430 			explain = TRUE;
431 		}
432 	}
433 
434 # ifdef USE_CURSES
435 	move(HEIGHT, 0);
436 # else
437 	mvcur(cur_row, cur_col, HEIGHT, 0);
438 	cur_row = HEIGHT;
439 	cur_col = 0;
440 # endif
441 # ifdef FLY
442 	put_str("Scan, Cloak, Flying, or Quit? ");
443 # else
444 	put_str("Scan, Cloak, or Quit? ");
445 # endif
446 	clear_eol();
447 	refresh();
448 	explain = FALSE;
449 	for (;;) {
450 		if (isupper(ch = getchar()))
451 			ch = tolower(ch);
452 		if (ch == 's')
453 			return Q_SCAN;
454 		else if (ch == 'c')
455 			return Q_CLOAK;
456 # ifdef FLY
457 		else if (ch == 'f')
458 			return Q_FLY;
459 # endif
460 		else if (ch == 'q')
461 			return Q_QUIT;
462 		beep();
463 		if (!explain) {
464 # ifdef FLY
465 			put_str("[SCFQ] ");
466 # else
467 			put_str("[SCQ] ");
468 # endif
469 			explain = TRUE;
470 		}
471 		refresh();
472 	}
473 }
474 
475 # ifndef USE_CURSES
476 void
477 put_ch(ch)
478 	char	ch;
479 {
480 	if (!isprint(ch)) {
481 		fprintf(stderr, "r,c,ch: %d,%d,%d", cur_row, cur_col, ch);
482 		return;
483 	}
484 	screen[cur_row][cur_col] = ch;
485 	putchar(ch);
486 	if (++cur_col >= COLS) {
487 		if (!AM || XN)
488 			putchar('\n');
489 		cur_col = 0;
490 		if (++cur_row >= LINES)
491 			cur_row = LINES;
492 	}
493 }
494 
495 void
496 put_str(s)
497 	char	*s;
498 {
499 	while (*s)
500 		put_ch(*s++);
501 }
502 # endif
503 
504 void
505 clear_the_screen()
506 {
507 # ifdef USE_CURSES
508 	clear();
509 	move(0, 0);
510 	refresh();
511 # else
512 	int	i;
513 
514 	if (blanks[0] == '\0')
515 		for (i = 0; i < SCREEN_WIDTH; i++)
516 			blanks[i] = ' ';
517 
518 	if (CL != NULL) {
519 #if !defined(BSD_RELEASE) || BSD_RELEASE < 44
520 		tputs(CL, LINES, _putchar);
521 #else
522 		tputs(CL, LINES, __cputchar);
523 #endif
524 		for (i = 0; i < SCREEN_HEIGHT; i++)
525 			memcpy(screen[i], blanks, SCREEN_WIDTH);
526 	} else {
527 		for (i = 0; i < SCREEN_HEIGHT; i++) {
528 			mvcur(cur_row, cur_col, i, 0);
529 			cur_row = i;
530 			cur_col = 0;
531 			clear_eol();
532 		}
533 		mvcur(cur_row, cur_col, 0, 0);
534 	}
535 	cur_row = cur_col = 0;
536 #endif
537 }
538 
539 #ifndef USE_CURSES
540 void
541 clear_eol()
542 {
543 	if (CE != NULL)
544 #if !defined(BSD_RELEASE) || BSD_RELEASE < 44
545 		tputs(CE, 1, _putchar);
546 #else
547 		tputs(CE, 1, __cputchar);
548 #endif
549 	else {
550 		fwrite(blanks, sizeof (char), SCREEN_WIDTH - cur_col, stdout);
551 		if (COLS != SCREEN_WIDTH)
552 			mvcur(cur_row, SCREEN_WIDTH, cur_row, cur_col);
553 		else if (AM)
554 			mvcur(cur_row + 1, 0, cur_row, cur_col);
555 		else
556 			mvcur(cur_row, SCREEN_WIDTH - 1, cur_row, cur_col);
557 	}
558 	memcpy(&screen[cur_row][cur_col], blanks, SCREEN_WIDTH - cur_col);
559 }
560 # endif
561 
562 void
563 redraw_screen()
564 {
565 # ifdef USE_CURSES
566 	clearok(stdscr, TRUE);
567 	touchwin(stdscr);
568 # else
569 	int		i;
570 # ifndef NOCURSES
571 	static int	first = 1;
572 
573 	if (first) {
574 		curscr = newwin(SCREEN_HEIGHT, SCREEN_WIDTH, 0, 0);
575 		if (curscr == NULL)
576 			errx(1, "Can't create curscr");
577 # if !defined(BSD_RELEASE) || BSD_RELEASE < 44
578 		for (i = 0; i < SCREEN_HEIGHT; i++)
579 			curscr->_y[i] = screen[i];
580 # endif
581 		first = 0;
582 	}
583 # if defined(BSD_RELEASE) && BSD_RELEASE >= 44
584 	for (i = 0; i < SCREEN_HEIGHT; i++) {
585 		int	j;
586 
587 		for (j = 0; j < SCREEN_WIDTH; j++)
588 			curscr->lines[i]->line[j].ch = screen[i][j];
589 	}
590 	curscr->cury = cur_row;
591 	curscr->curx = cur_col;
592 # else
593 	curscr->_cury = cur_row;
594 	curscr->_curx = cur_col;
595 # endif
596 	clearok(curscr, TRUE);
597 	touchwin(curscr);
598 	wrefresh(curscr);
599 #else
600 	mvcur(cur_row, cur_col, 0, 0);
601 	for (i = 0; i < SCREEN_HEIGHT - 1; i++) {
602 		fwrite(screen[i], sizeof (char), SCREEN_WIDTH, stdout);
603 		if (COLS > SCREEN_WIDTH || (COLS == SCREEN_WIDTH && !AM))
604 			putchar('\n');
605 	}
606 	fwrite(screen[SCREEN_HEIGHT - 1], sizeof (char), SCREEN_WIDTH - 1,
607 		stdout);
608 	mvcur(SCREEN_HEIGHT - 1, SCREEN_WIDTH - 1, cur_row, cur_col);
609 #endif
610 #endif
611 }
612 
613 /*
614  * do_message:
615  *	Send a message to the driver and return
616  */
617 void
618 do_message()
619 {
620 	long	version;
621 
622 	if (read(Socket, (char *) &version, LONGLEN) != LONGLEN) {
623 		bad_con();
624 		/* NOTREACHED */
625 	}
626 	if (ntohl(version) != HUNT_VERSION) {
627 		bad_ver();
628 		/* NOTREACHED */
629 	}
630 # ifdef INTERNET
631 	if (write(Socket, Send_message, strlen(Send_message)) < 0) {
632 		bad_con();
633 		/* NOTREACHED */
634 	}
635 # endif
636 	(void) close(Socket);
637 }
638