xref: /dragonfly/games/phantasia/io.c (revision 4f91f2b658e9de751719e25f0c9507f2e9afec27)
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
getstring(char * cp,int mx)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
more(int where)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
infloat(void)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
inputoption(void)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
interrupt(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
getanswer(const char * choices,bool def)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
catchalarm(__unused int sig)331 catchalarm(__unused int sig)
332 {
333           longjmp(Timeoenv, 1);
334 }
335