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