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