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