xref: /dragonfly/games/hunt/hunt/playit.c (revision 479ab7f0)
1 /*-
2  * Copyright (c) 1983-2003, Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the University of California, San Francisco nor
15  *    the names of its contributors may be used to endorse or promote
16  *    products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $OpenBSD: playit.c,v 1.8 2003/06/11 08:45:25 pjanzen Exp $
32  * $NetBSD: playit.c,v 1.4 1997/10/20 00:37:15 lukem Exp $
33  */
34 
35 #include <sys/types.h>
36 #include <sys/file.h>
37 #include <arpa/inet.h>
38 
39 #include <err.h>
40 #include <errno.h>
41 #include <ctype.h>
42 #include <termios.h>
43 #include <signal.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <stdio.h>
47 #include "hunt.h"
48 #include "display.h"
49 #include "client.h"
50 
51 static int	nchar_send;
52 static FLAG	Last_player;
53 static int	Otto_expect;
54 
55 # define	MAX_SEND	5
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 static	unsigned char	getchr(void);
68 static	void		send_stuff(void);
69 
70 /*
71  * playit:
72  *	Play a given game, handling all the curses commands from
73  *	the driver.
74  */
75 void
76 playit(void)
77 {
78 	int		ch;
79 	int		y, x;
80 	u_int32_t	version;
81 	int		otto_y, otto_x;
82 	char		otto_face = ' ';
83 	int		chars_processed;
84 
85 	if (read(Socket, &version, sizeof version) != sizeof version) {
86 		bad_con();
87 		/* NOTREACHED */
88 	}
89 	if (ntohl(version) != (unsigned int)HUNT_VERSION) {
90 		bad_ver();
91 		/* NOTREACHED */
92 	}
93 	errno = 0;
94 	nchar_send = MAX_SEND;
95 	Otto_expect = 0;
96 	while ((ch = GETCHR()) != EOF) {
97 		switch (ch & 0377) {
98 		  case MOVE:
99 			y = GETCHR();
100 			x = GETCHR();
101 			display_move(y, x);
102 			break;
103 
104 		  case CLRTOEOL:
105 			display_clear_eol();
106 			break;
107 		  case CLEAR:
108 			display_clear_the_screen();
109 			break;
110 		  case REFRESH:
111 			display_refresh();
112 			break;
113 		  case REDRAW:
114 			display_redraw_screen();
115 			display_refresh();
116 			break;
117 		  case ENDWIN:
118 			display_refresh();
119 			if ((ch = GETCHR()) == LAST_PLAYER)
120 				Last_player = TRUE;
121 			ch = EOF;
122 			goto out;
123 		  case BELL:
124 			display_beep();
125 			break;
126 		  case READY:
127 			chars_processed = GETCHR();
128 			display_refresh();
129 			if (nchar_send < 0)
130 				tcflush(STDIN_FILENO, TCIFLUSH);
131 			nchar_send = MAX_SEND;
132 			if (Otto_mode) {
133 				/*
134 				 * The driver returns the number of keypresses
135 				 * that it has processed. Use this to figure
136 				 * out if otto's commands have completed.
137 				 */
138 				Otto_expect -= chars_processed;
139 				if (Otto_expect == 0) {
140 					/* not very fair! */
141 					static char buf[MAX_SEND * 2];
142 					int len;
143 
144 					/* Ask otto what it wants to do: */
145 					len = otto(otto_y, otto_x, otto_face,
146 						buf, sizeof buf);
147 					if (len) {
148 						/* Pass it on to the driver: */
149 						write(Socket, buf, len);
150 						/* Update expectations: */
151 						Otto_expect += len;
152 					}
153 				}
154 			}
155 			break;
156 		  case ADDCH:
157 			ch = GETCHR();
158 			/* FALLTHROUGH */
159 		  default:
160 			if (!isprint(ch))
161 				ch = ' ';
162 			display_put_ch(ch);
163 			if (Otto_mode)
164 				switch (ch) {
165 				case '<':
166 				case '>':
167 				case '^':
168 				case 'v':
169 					otto_face = ch;
170 					display_getyx(&otto_y, &otto_x);
171 					otto_x--;
172 					break;
173 				}
174 			break;
175 		}
176 	}
177 out:
178 	close(Socket);
179 }
180 
181 /*
182  * getchr:
183  *	Grab input and pass it along to the driver
184  *	Return any characters from the driver
185  *	When this routine is called by GETCHR, we already know there are
186  *	no characters in the input buffer.
187  */
188 static unsigned char
189 getchr(void)
190 {
191 	fd_set	readfds, s_readfds;
192 	int	nfds, s_nfds;
193 
194 	FD_ZERO(&s_readfds);
195 	FD_SET(Socket, &s_readfds);
196 	FD_SET(STDIN_FILENO, &s_readfds);
197 	s_nfds = (Socket > STDIN_FILENO) ? Socket : STDIN_FILENO;
198 	s_nfds++;
199 
200 one_more_time:
201 	do {
202 		errno = 0;
203 		readfds = s_readfds;
204 		nfds = s_nfds;
205 		nfds = select(nfds, &readfds, NULL, NULL, NULL);
206 	} while (nfds <= 0 && errno == EINTR);
207 
208 	if (FD_ISSET(STDIN_FILENO, &readfds))
209 		send_stuff();
210 	if (!FD_ISSET(Socket, &readfds))
211 		goto one_more_time;
212 	icnt = read(Socket, ibuf, sizeof ibuf);
213 	if (icnt <= 0) {
214 		bad_con();
215 		/* NOTREACHED */
216 	}
217 	iptr = ibuf;
218 	icnt--;
219 	return *iptr++;
220 }
221 
222 /*
223  * send_stuff:
224  *	Send standard input characters to the driver
225  */
226 static void
227 send_stuff(void)
228 {
229 	int		count;
230 	char		*sp, *nsp;
231 	static char	inp[BUFSIZ];
232 	static char	Buf[BUFSIZ];
233 
234 	/* Drain the user's keystrokes: */
235 	count = read(STDIN_FILENO, Buf, sizeof Buf);
236 	if (count < 0)
237 		err(1, "read");
238 	if (count == 0)
239 		return;
240 
241 	if (nchar_send <= 0 && !no_beep) {
242 		display_beep();
243 		return;
244 	}
245 
246 	/*
247 	 * look for 'q'uit commands; if we find one,
248 	 * confirm it.  If it is not confirmed, strip
249 	 * it out of the input
250 	 */
251 	Buf[count] = '\0';
252 	for (sp = Buf, nsp = inp; *sp != '\0'; sp++, nsp++) {
253 		*nsp = map_key[(int)*sp];
254 		if (*nsp == 'q')
255 			intr(0);
256 	}
257 	count = nsp - inp;
258 	if (count) {
259 		nchar_send -= count;
260 		if (nchar_send < 0)
261 			count += nchar_send;
262 		write(Socket, inp, count);
263 		if (Otto_mode) {
264 			/*
265 			 * The user can insert commands over otto.
266 			 * So, otto shouldn't be alarmed when the
267 			 * server processes more than otto asks for.
268 			 */
269 			Otto_expect += count;
270 		}
271 	}
272 }
273 
274 /*
275  * quit:
276  *	Handle the end of the game when the player dies
277  */
278 int
279 quit(int old_status)
280 {
281 	int	explain, ch;
282 
283 	if (Last_player)
284 		return Q_QUIT;
285 	if (Otto_mode)
286 		return otto_quit(old_status);
287 	display_move(HEIGHT, 0);
288 	display_put_str("Re-enter game [ynwo]? ");
289 	display_clear_eol();
290 	explain = FALSE;
291 	for (;;) {
292 		display_refresh();
293 		if (isupper(ch = getchar()))
294 			ch = tolower(ch);
295 		if (ch == 'y')
296 			return old_status;
297 		else if (ch == 'o')
298 			break;
299 		else if (ch == 'n') {
300 			display_move(HEIGHT, 0);
301 			display_put_str("Write a parting message [yn]? ");
302 			display_clear_eol();
303 			display_refresh();
304 			for (;;) {
305 				if (isupper(ch = getchar()))
306 					ch = tolower(ch);
307 				if (ch == 'y')
308 					goto get_message;
309 				if (ch == 'n')
310 					return Q_QUIT;
311 			}
312 		}
313 		else if (ch == 'w') {
314 			static	char	buf[WIDTH + WIDTH % 2];
315 			char		*cp, c;
316 
317 get_message:
318 			c = ch;		/* save how we got here */
319 			display_move(HEIGHT, 0);
320 			display_put_str("Message: ");
321 			display_clear_eol();
322 			display_refresh();
323 			cp = buf;
324 			for (;;) {
325 				display_refresh();
326 				if ((ch = getchar()) == '\n' || ch == '\r')
327 					break;
328 				if (display_iserasechar(ch))
329 				{
330 					if (cp > buf) {
331 						int y, x;
332 
333 						display_getyx(&y, &x);
334 						display_move(y, x - 1);
335 						cp -= 1;
336 						display_clear_eol();
337 					}
338 					continue;
339 				}
340 				else if (display_iskillchar(ch))
341 				{
342 					int y, x;
343 
344 					display_getyx(&y, &x);
345 					display_move(y, x - (cp - buf));
346 					cp = buf;
347 					display_clear_eol();
348 					continue;
349 				} else if (!isprint(ch)) {
350 					display_beep();
351 					continue;
352 				}
353 				display_put_ch(ch);
354 				*cp++ = ch;
355 				if (cp + 1 >= buf + sizeof buf)
356 					break;
357 			}
358 			*cp = '\0';
359 			Send_message = buf;
360 			return (c == 'w') ? old_status : Q_MESSAGE;
361 		}
362 		display_beep();
363 		if (!explain) {
364 			display_put_str("(Yes, No, Write message, or Options) ");
365 			explain = TRUE;
366 		}
367 	}
368 
369 	display_move(HEIGHT, 0);
370 	display_put_str("Scan, Cloak, Flying, or Quit? ");
371 	display_clear_eol();
372 	display_refresh();
373 	explain = FALSE;
374 	for (;;) {
375 		if (isupper(ch = getchar()))
376 			ch = tolower(ch);
377 		if (ch == 's')
378 			return Q_SCAN;
379 		else if (ch == 'c')
380 			return Q_CLOAK;
381 		else if (ch == 'f')
382 			return Q_FLY;
383 		else if (ch == 'q')
384 			return Q_QUIT;
385 		display_beep();
386 		if (!explain) {
387 			display_put_str("[SCFQ] ");
388 			explain = TRUE;
389 		}
390 		display_refresh();
391 	}
392 }
393 
394 /*
395  * do_message:
396  *	Send a message to the driver and return
397  */
398 void
399 do_message(void)
400 {
401 	u_int32_t	version;
402 
403 	if (read(Socket, &version, sizeof version) != sizeof version) {
404 		bad_con();
405 		/* NOTREACHED */
406 	}
407 	if (ntohl(version) != (unsigned int)HUNT_VERSION) {
408 		bad_ver();
409 		/* NOTREACHED */
410 	}
411 	if (write(Socket, Send_message, strlen(Send_message)) < 0) {
412 		bad_con();
413 		/* NOTREACHED */
414 	}
415 	close(Socket);
416 }
417