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