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