1 /* $OpenBSD: io.c,v 1.10 2021/04/29 01:57:00 millert Exp $ */ 2 /* $NetBSD: io.c,v 1.2 1995/03/24 03:58:50 cgd Exp $ */ 3 4 /* 5 * io.c - input/output routines for Phantasia 6 */ 7 8 #include <ctype.h> 9 #include <curses.h> 10 #include <math.h> 11 #include <setjmp.h> 12 #include <signal.h> 13 #include <string.h> 14 #include <unistd.h> 15 16 #include "macros.h" 17 #include "phantdefs.h" 18 #include "phantglobs.h" 19 20 static jmp_buf Timeoenv; /* used for timing out waiting for input */ 21 22 /************************************************************************ 23 / 24 / FUNCTION NAME: getstring() 25 / 26 / FUNCTION: read a string from operator 27 / 28 / AUTHOR: E. A. Estes, 12/4/85 29 / 30 / ARGUMENTS: 31 / char *cp - pointer to buffer area to fill 32 / int mx - maximum number of characters to put in buffer 33 / 34 / RETURN VALUE: none 35 / 36 / MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(), 37 / wclrtoeol() 38 / 39 / GLOBAL INPUTS: Echo, _iob[], Wizard, *stdscr 40 / 41 / GLOBAL OUTPUTS: _iob[] 42 / 43 / DESCRIPTION: 44 / Read a string from the keyboard. 45 / This routine is specially designed to: 46 / 47 / - strip non-printing characters (unless Wizard) 48 / - echo, if desired 49 / - redraw the screen if CH_REDRAW is entered 50 / - read in only 'mx - 1' characters or less characters 51 / - nul-terminate string, and throw away newline 52 / 53 / 'mx' is assumed to be at least 2. 54 / 55 *************************************************************************/ 56 57 void 58 getstring(char *cp, int mx) 59 { 60 char *inptr; /* pointer into string for next string */ 61 int x, y; /* original x, y coordinates on screen */ 62 int ch; /* input */ 63 64 getyx(stdscr, y, x); /* get coordinates on screen */ 65 inptr = cp; 66 *inptr = '\0'; /* clear string to start */ 67 --mx; /* reserve room in string for nul terminator */ 68 69 do 70 /* get characters and process */ 71 { 72 if (Echo) 73 mvaddstr(y, x, cp); /* print string on screen */ 74 clrtoeol(); /* clear any data after string */ 75 refresh(); /* update screen */ 76 77 ch = getchar(); /* get character */ 78 79 switch (ch) { 80 case CH_NEWLINE: /* terminate string */ 81 case CH_RETURN: 82 break; 83 84 case CH_REDRAW: /* redraw screen */ 85 clearok(stdscr, TRUE); 86 continue; 87 88 default: /* put data in string */ 89 if (ch == Ch_Erase) { /* back up one character */ 90 if (inptr > cp) 91 --inptr; 92 break; 93 } else if (ch == Ch_Kill) { /* back up to original location */ 94 inptr = cp; 95 break; 96 } else if (isprint(ch) || Wizard) { 97 /* printing char; put in string */ 98 *inptr++ = ch; 99 } 100 } 101 102 *inptr = '\0'; /* terminate string */ 103 } 104 while (ch != CH_NEWLINE && ch != CH_RETURN && inptr < cp + mx); 105 } 106 /**/ 107 /************************************************************************ 108 / 109 / FUNCTION NAME: more() 110 / 111 / FUNCTION: pause and prompt player 112 / 113 / AUTHOR: E. A. Estes, 12/4/85 114 / 115 / ARGUMENTS: 116 / int where - line on screen on which to pause 117 / 118 / RETURN VALUE: none 119 / 120 / MODULES CALLED: wmove(), waddstr(), getanswer() 121 / 122 / GLOBAL INPUTS: *stdscr 123 / 124 / GLOBAL OUTPUTS: none 125 / 126 / DESCRIPTION: 127 / Print a message, and wait for a space character. 128 / 129 *************************************************************************/ 130 131 void 132 more(int where) 133 { 134 mvaddstr(where, 0, "-- more --"); 135 getanswer(" ", FALSE); 136 } 137 /**/ 138 /************************************************************************ 139 / 140 / FUNCTION NAME: infloat() 141 / 142 / FUNCTION: input a floating point number from operator 143 / 144 / AUTHOR: E. A. Estes, 12/4/85 145 / 146 / ARGUMENTS: none 147 / 148 / RETURN VALUE: floating point number from operator 149 / 150 / MODULES CALLED: sscanf(), getstring() 151 / 152 / GLOBAL INPUTS: Databuf[] 153 / 154 / GLOBAL OUTPUTS: none 155 / 156 / DESCRIPTION: 157 / Read a string from player, and scan for a floating point 158 / number. 159 / If no valid number is found, return 0.0. 160 / 161 *************************************************************************/ 162 163 double 164 infloat(void) 165 { 166 double result; /* return value */ 167 168 getstring(Databuf, SZ_DATABUF); 169 if (sscanf(Databuf, "%lf", &result) < 1) 170 /* no valid number entered */ 171 result = 0.0; 172 173 return (result); 174 } 175 /**/ 176 /************************************************************************ 177 / 178 / FUNCTION NAME: inputoption() 179 / 180 / FUNCTION: input an option value from player 181 / 182 / AUTHOR: E. A. Estes, 12/4/85 183 / 184 / ARGUMENTS: none 185 / 186 / RETURN VALUE: none 187 / 188 / MODULES CALLED: floor(), drandom(), getanswer() 189 / 190 / GLOBAL INPUTS: Player 191 / 192 / GLOBAL OUTPUTS: Player 193 / 194 / DESCRIPTION: 195 / Age increases with every move. 196 / Refresh screen, and get a single character option from player. 197 / Return a random value if player's ring has gone bad. 198 / 199 *************************************************************************/ 200 201 int 202 inputoption(void) 203 { 204 ++Player.p_age; /* increase age */ 205 206 if (Player.p_ring.ring_type != R_SPOILED) 207 /* ring ok */ 208 return (getanswer("T ", TRUE)); 209 else 210 /* bad ring */ 211 { 212 getanswer(" ", TRUE); 213 return ((int) ROLL(0.0, 5.0) + '0'); 214 } 215 } 216 /**/ 217 /************************************************************************ 218 / 219 / FUNCTION NAME: interrupt() 220 / 221 / FUNCTION: handle interrupt from operator 222 / 223 / AUTHOR: E. A. Estes, 12/4/85 224 / 225 / ARGUMENTS: none 226 / 227 / RETURN VALUE: none 228 / 229 / MODULES CALLED: fork(), exit(), wait(), death(), alarm(), execl(), wmove(), 230 / signal(), getenv(), wclear(), crmode(), clearok(), waddstr(), 231 / cleanup(), wrefresh(), leavegame(), getanswer() 232 / 233 / GLOBAL INPUTS: Player, *stdscr 234 / 235 / GLOBAL OUTPUTS: none 236 / 237 / DESCRIPTION: 238 / Allow player to quit upon hitting the interrupt key. 239 / If the player wants to quit while in battle, he/she automatically 240 / dies. 241 / 242 *************************************************************************/ 243 244 void 245 interrupt(void) 246 { 247 char line[81]; /* a place to store data already on screen */ 248 int loop; /* counter */ 249 int x, y; /* coordinates on screen */ 250 int ch; /* input */ 251 unsigned savealarm; /* to save alarm value */ 252 253 savealarm = alarm(0); /* turn off any alarms */ 254 255 getyx(stdscr, y, x); /* save cursor location */ 256 257 for (loop = 0; loop < 80; ++loop) { /* save line on screen */ 258 move(4, loop); 259 line[loop] = inch(); 260 } 261 line[80] = '\0'; /* nul terminate */ 262 263 if (Player.p_status == S_INBATTLE || Player.p_status == S_MONSTER) 264 /* in midst of fighting */ 265 { 266 mvaddstr(4, 0, "Quitting now will automatically kill your character. Still want to ? "); 267 ch = getanswer("NY", FALSE); 268 if (ch == 'Y') 269 death("Bailing out"); 270 } else { 271 mvaddstr(4, 0, "Do you really want to quit ? "); 272 ch = getanswer("NY", FALSE); 273 if (ch == 'Y') 274 leavegame(); 275 } 276 277 mvaddstr(4, 0, line); /* restore data on screen */ 278 move(y, x); /* restore cursor */ 279 refresh(); 280 281 alarm(savealarm); /* restore alarm */ 282 } 283 /**/ 284 /************************************************************************ 285 / 286 / FUNCTION NAME: getanswer() 287 / 288 / FUNCTION: get an answer from operator 289 / 290 / AUTHOR: E. A. Estes, 12/4/85 291 / 292 / ARGUMENTS: 293 / char *choices - string of (upper case) valid choices 294 / bool def - set if default answer 295 / 296 / RETURN VALUE: none 297 / 298 / MODULES CALLED: alarm(), wmove(), waddch(), signal(), setjmp(), strchr(), 299 / _filbuf(), clearok(), toupper(), wrefresh(), mvprintw(), wclrtoeol() 300 / 301 / GLOBAL INPUTS: catchalarm(), Echo, _iob[], _ctype[], *stdscr, Timeout, 302 / Timeoenv[] 303 / 304 / GLOBAL OUTPUTS: _iob[] 305 / 306 / DESCRIPTION: 307 / Get a single character answer from operator. 308 / Timeout waiting for response. If we timeout, or the 309 / answer in not in the list of valid choices, print choices, 310 / and wait again, otherwise return the first character in ths 311 / list of choices. 312 / Give up after 3 tries. 313 / 314 *************************************************************************/ 315 316 int 317 getanswer(char *choices, bool def) 318 { 319 int ch; /* input */ 320 volatile int loop; /* counter */ 321 volatile int oldx, oldy; /* original coordinates on screen */ 322 323 getyx(stdscr, oldy, oldx); 324 alarm(0); /* make sure alarm is off */ 325 326 for (loop = 3; loop; --loop) 327 /* try for 3 times */ 328 { 329 if (setjmp(Timeoenv) != 0) 330 /* timed out waiting for response */ 331 { 332 if (def || loop <= 1) 333 /* return default answer */ 334 break; 335 else 336 /* prompt, and try again */ 337 goto YELL; 338 } else 339 /* wait for response */ 340 { 341 clrtoeol(); 342 refresh(); 343 signal(SIGALRM, catchalarm); 344 /* set timeout */ 345 if (Timeout) 346 alarm(7); /* short */ 347 else 348 alarm(600); /* long */ 349 350 ch = getchar(); 351 352 alarm(0); /* turn off timeout */ 353 354 if (ch < 0) { 355 /* caught some signal */ 356 ++loop; 357 continue; 358 } else if (ch == CH_REDRAW) { 359 /* redraw screen */ 360 clearok(stdscr, TRUE); /* force clear screen */ 361 ++loop; /* don't count this input */ 362 continue; 363 } else if (Echo) { 364 addch(ch); /* echo character */ 365 refresh(); 366 } 367 if (islower(ch)) 368 /* convert to upper case */ 369 ch = toupper(ch); 370 371 if (def || strchr(choices, ch) != NULL) 372 /* valid choice */ 373 return (ch); 374 else if (!def && loop > 1) { 375 /* bad choice; prompt, and try again */ 376 YELL: mvprintw(oldy + 1, 0, "Please choose one of : [%s]\n", choices); 377 move(oldy, oldx); 378 clrtoeol(); 379 continue; 380 } else 381 /* return default answer */ 382 break; 383 } 384 } 385 386 return (*choices); 387 } 388 /**/ 389 /************************************************************************ 390 / 391 / FUNCTION NAME: catchalarm() 392 / 393 / FUNCTION: catch timer when waiting for input 394 / 395 / AUTHOR: E. A. Estes, 12/4/85 396 / 397 / ARGUMENTS: none 398 / 399 / RETURN VALUE: none 400 / 401 / MODULES CALLED: longjmp() 402 / 403 / GLOBAL INPUTS: Timeoenv[] 404 / 405 / GLOBAL OUTPUTS: none 406 / 407 / DESCRIPTION: 408 / Come here when the alarm expires while waiting for input. 409 / Simply longjmp() into getanswer(). 410 / 411 *************************************************************************/ 412 413 void 414 catchalarm(int dummy) 415 { 416 longjmp(Timeoenv, 1); 417 } 418